0525b更新

ModbusPack
“zcw” 3 years ago
parent 6c5f0539e6
commit feb5beabe4

1
.gitignore vendored

@ -16,3 +16,4 @@ o
*.spec
Static/license.lic
.idea/.gitignore
/Static/influxDB/engine/

@ -0,0 +1,4 @@
engine-path: "C:\\Users\\zcw\\Desktop\\DCS\\Static\\influxDB\\engine"
http-bind-address: ":6324"
storage-cache-max-memory-size: 268435456
bolt-path: "C:\\Users\\zcw\\Desktop\\DCS\\Static\\influxDB\\engine\\influxd.bolt"

@ -0,0 +1,241 @@
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\<service>\Parameters\AppExit\<n>,
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.

@ -0,0 +1,692 @@
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.
Usage
-----
In the usage notes below, arguments to the program may be written in angle
brackets and/or square brackets. <string> means you must insert the
appropriate string and [<string>] means the string is optional. See the
examples below...
Note that everywhere <servicename> appears you may substitute the
service's display name.
Installation using the GUI
--------------------------
To install a service, run
nssm install <servicename>
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 <servicename> <application> [<options>]
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\<service>\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\<service>\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\<service>\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\<service>\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\<service>\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\<service>\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\<service>\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.
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\<service>\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\<service>\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\<service>\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.
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 <servicename>
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.
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\<service>\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.
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 <servicename>
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 <servicename> <parameter>
nssm set <servicename> <parameter> <value>
Parameters can also be reset to their default values.
nssm reset <servicename> <parameter>
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.
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\<service>.
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 <servicename> Description "NSSM managed service"
nssm set <servicename> Description NSSM managed service
Non-standard parameters
-----------------------
The AppEnvironment and AppEnvironmentExtra parameters recognise an
additional argument when querying the environment. The following syntax
will print all extra environment variables configured for a service
nssm get <servicename> 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 <servicename> 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 <servicename> AppEnvironment CLASSPATH=C:\Classes 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 <servicename> AppExit Default
To get the exit action when the application exits with exit code 2, run
nssm get <servicename> 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 <servicename> 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 <servicename> DependOnService RpcSs LanmanWorkstation
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 <servicename>.
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 <servicename> ObjectName
To set the username and password, run
nssm set <servicename> ObjectName <username> <password>
Note that the rules of argument concatenation still apply. The following
invocation is valid and will have the expected effect.
nssm set <servicename> ObjectName <username> 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"
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 <servicename> ObjectName
nssm set <servicename> Type SERVICE_INTERACTIVE_PROCESS
Controlling services using the command line
-------------------------------------------
NSSM offers rudimentary service control features.
nssm start <servicename>
nssm restart <servicename>
nssm stop <servicename>
nssm status <servicename>
Removing services using the GUI
-------------------------------
NSSM can also remove services. Run
nssm remove <servicename>
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 <servicename> 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.
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().
Licence
-------
NSSM is public domain. You may unconditionally use it and/or its source code
for any purpose you wish.

@ -0,0 +1,346 @@
#include "nssm.h"
#include <sddl.h>
extern imports_t imports;
/* Open Policy object. */
int open_lsa_policy(LSA_HANDLE *policy) {
LSA_OBJECT_ATTRIBUTES attributes;
ZeroMemory(&attributes, sizeof(attributes));
NTSTATUS status = LsaOpenPolicy(0, &attributes, POLICY_ALL_ACCESS, policy);
if (status) {
print_message(stderr, NSSM_MESSAGE_LSAOPENPOLICY_FAILED, error_string(LsaNtStatusToWinError(status)));
return 1;
}
return 0;
}
/* Look up SID for an account. */
int username_sid(const TCHAR *username, SID **sid, LSA_HANDLE *policy) {
LSA_HANDLE handle;
if (! policy) {
policy = &handle;
if (open_lsa_policy(policy)) return 1;
}
/*
LsaLookupNames() can't look up .\username but can look up
%COMPUTERNAME%\username. ChangeServiceConfig() writes .\username to the
registry when %COMPUTERNAME%\username is a passed as a parameter. We
need to preserve .\username when calling ChangeServiceConfig() without
changing the username, but expand to %COMPUTERNAME%\username when calling
LsaLookupNames().
*/
TCHAR *expanded;
unsigned long expandedlen;
if (_tcsnicmp(_T(".\\"), username, 2)) {
expandedlen = (unsigned long) (_tcslen(username) + 1) * sizeof(TCHAR);
expanded = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, expandedlen);
if (! expanded) {
print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("expanded"), _T("username_sid"));
if (policy == &handle) LsaClose(handle);
return 2;
}
memmove(expanded, username, expandedlen);
}
else {
TCHAR computername[MAX_COMPUTERNAME_LENGTH + 1];
expandedlen = _countof(computername);
GetComputerName(computername, &expandedlen);
expandedlen += (unsigned long) _tcslen(username);
expanded = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, expandedlen * sizeof(TCHAR));
if (! expanded) {
print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("expanded"), _T("username_sid"));
if (policy == &handle) LsaClose(handle);
return 2;
}
_sntprintf_s(expanded, expandedlen, _TRUNCATE, _T("%s\\%s"), computername, username + 2);
}
LSA_UNICODE_STRING lsa_username;
#ifdef UNICODE
lsa_username.Buffer = (wchar_t *) expanded;
lsa_username.Length = (unsigned short) _tcslen(expanded) * sizeof(TCHAR);
lsa_username.MaximumLength = lsa_username.Length + sizeof(TCHAR);
#else
size_t buflen;
mbstowcs_s(&buflen, NULL, 0, expanded, _TRUNCATE);
lsa_username.MaximumLength = (unsigned short) buflen * sizeof(wchar_t);
lsa_username.Length = lsa_username.MaximumLength - sizeof(wchar_t);
lsa_username.Buffer = (wchar_t *) HeapAlloc(GetProcessHeap(), 0, lsa_username.MaximumLength);
if (lsa_username.Buffer) mbstowcs_s(&buflen, lsa_username.Buffer, lsa_username.MaximumLength, expanded, _TRUNCATE);
else {
if (policy == &handle) LsaClose(handle);
HeapFree(GetProcessHeap(), 0, expanded);
print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("LSA_UNICODE_STRING"), _T("username_sid()"));
return 4;
}
#endif
LSA_REFERENCED_DOMAIN_LIST *translated_domains;
LSA_TRANSLATED_SID *translated_sid;
NTSTATUS status = LsaLookupNames(*policy, 1, &lsa_username, &translated_domains, &translated_sid);
#ifndef UNICODE
HeapFree(GetProcessHeap(), 0, lsa_username.Buffer);
#endif
HeapFree(GetProcessHeap(), 0, expanded);
if (policy == &handle) LsaClose(handle);
if (status) {
LsaFreeMemory(translated_domains);
LsaFreeMemory(translated_sid);
print_message(stderr, NSSM_MESSAGE_LSALOOKUPNAMES_FAILED, username, error_string(LsaNtStatusToWinError(status)));
return 5;
}
if (translated_sid->Use != SidTypeUser && translated_sid->Use != SidTypeWellKnownGroup) {
LsaFreeMemory(translated_domains);
LsaFreeMemory(translated_sid);
print_message(stderr, NSSM_GUI_INVALID_USERNAME, username);
return 6;
}
LSA_TRUST_INFORMATION *trust = &translated_domains->Domains[translated_sid->DomainIndex];
if (! trust || ! IsValidSid(trust->Sid)) {
LsaFreeMemory(translated_domains);
LsaFreeMemory(translated_sid);
print_message(stderr, NSSM_GUI_INVALID_USERNAME, username);
return 7;
}
/* GetSidSubAuthority*() return pointers! */
unsigned char *n = GetSidSubAuthorityCount(trust->Sid);
/* Convert translated SID to SID. */
*sid = (SID *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, GetSidLengthRequired(*n + 1));
if (! *sid) {
LsaFreeMemory(translated_domains);
LsaFreeMemory(translated_sid);
print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SID"), _T("username_sid"));
return 8;
}
unsigned long error;
if (! InitializeSid(*sid, GetSidIdentifierAuthority(trust->Sid), *n + 1)) {
error = GetLastError();
HeapFree(GetProcessHeap(), 0, *sid);
LsaFreeMemory(translated_domains);
LsaFreeMemory(translated_sid);
print_message(stderr, NSSM_MESSAGE_INITIALIZESID_FAILED, username, error_string(error));
return 9;
}
for (unsigned char i = 0; i <= *n; i++) {
unsigned long *sub = GetSidSubAuthority(*sid, i);
if (i < *n) *sub = *GetSidSubAuthority(trust->Sid, i);
else *sub = translated_sid->RelativeId;
}
int ret = 0;
if (translated_sid->Use == SidTypeWellKnownGroup && ! well_known_sid(*sid)) {
print_message(stderr, NSSM_GUI_INVALID_USERNAME, username);
ret = 10;
}
LsaFreeMemory(translated_domains);
LsaFreeMemory(translated_sid);
return ret;
}
int username_sid(const TCHAR *username, SID **sid) {
return username_sid(username, sid, 0);
}
int canonicalise_username(const TCHAR *username, TCHAR **canon) {
LSA_HANDLE policy;
if (open_lsa_policy(&policy)) return 1;
SID *sid;
if (username_sid(username, &sid, &policy)) return 2;
PSID sids = { sid };
LSA_REFERENCED_DOMAIN_LIST *translated_domains;
LSA_TRANSLATED_NAME *translated_name;
NTSTATUS status = LsaLookupSids(policy, 1, &sids, &translated_domains, &translated_name);
if (status) {
LsaFreeMemory(translated_domains);
LsaFreeMemory(translated_name);
print_message(stderr, NSSM_MESSAGE_LSALOOKUPSIDS_FAILED, error_string(LsaNtStatusToWinError(status)));
return 3;
}
LSA_TRUST_INFORMATION *trust = &translated_domains->Domains[translated_name->DomainIndex];
LSA_UNICODE_STRING lsa_canon;
lsa_canon.Length = translated_name->Name.Length + trust->Name.Length + sizeof(wchar_t);
lsa_canon.MaximumLength = lsa_canon.Length + sizeof(wchar_t);
lsa_canon.Buffer = (wchar_t *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lsa_canon.MaximumLength);
if (! lsa_canon.Buffer) {
LsaFreeMemory(translated_domains);
LsaFreeMemory(translated_name);
print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("lsa_canon"), _T("username_sid"));
return 9;
}
/* Buffer is wchar_t but Length is in bytes. */
memmove((char *) lsa_canon.Buffer, trust->Name.Buffer, trust->Name.Length);
memmove((char *) lsa_canon.Buffer + trust->Name.Length, L"\\", sizeof(wchar_t));
memmove((char *) lsa_canon.Buffer + trust->Name.Length + sizeof(wchar_t), translated_name->Name.Buffer, translated_name->Name.Length);
#ifdef UNICODE
*canon = lsa_canon.Buffer;
#else
size_t buflen;
wcstombs_s(&buflen, NULL, 0, lsa_canon.Buffer, _TRUNCATE);
*canon = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, buflen);
if (! *canon) {
LsaFreeMemory(translated_domains);
LsaFreeMemory(translated_name);
print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canon"), _T("username_sid"));
return 10;
}
wcstombs_s(&buflen, *canon, buflen, lsa_canon.Buffer, _TRUNCATE);
HeapFree(GetProcessHeap(), 0, lsa_canon.Buffer);
#endif
LsaFreeMemory(translated_domains);
LsaFreeMemory(translated_name);
return 0;
}
/* Do two usernames map to the same SID? */
int username_equiv(const TCHAR *a, const TCHAR *b) {
SID *sid_a, *sid_b;
if (username_sid(a, &sid_a)) return 0;
if (username_sid(b, &sid_b)) {
FreeSid(sid_a);
return 0;
}
int ret = 0;
if (EqualSid(sid_a, sid_b)) ret = 1;
FreeSid(sid_a);
FreeSid(sid_b);
return ret;
}
/* Does the username represent the LocalSystem account? */
int is_localsystem(const TCHAR *username) {
if (str_equiv(username, NSSM_LOCALSYSTEM_ACCOUNT)) return 1;
if (! imports.IsWellKnownSid) return 0;
SID *sid;
if (username_sid(username, &sid)) return 0;
int ret = 0;
if (imports.IsWellKnownSid(sid, WinLocalSystemSid)) ret = 1;
FreeSid(sid);
return ret;
}
/*
Get well-known alias for LocalSystem and friends.
Returns a pointer to a static string. DO NOT try to free it.
*/
const TCHAR *well_known_sid(SID *sid) {
if (! imports.IsWellKnownSid) return 0;
if (imports.IsWellKnownSid(sid, WinLocalSystemSid)) return NSSM_LOCALSYSTEM_ACCOUNT;
if (imports.IsWellKnownSid(sid, WinLocalServiceSid)) return NSSM_LOCALSERVICE_ACCOUNT;
if (imports.IsWellKnownSid(sid, WinNetworkServiceSid)) return NSSM_NETWORKSERVICE_ACCOUNT;
return 0;
}
const TCHAR *well_known_username(const TCHAR *username) {
if (! username) return NSSM_LOCALSYSTEM_ACCOUNT;
if (str_equiv(username, NSSM_LOCALSYSTEM_ACCOUNT)) return NSSM_LOCALSYSTEM_ACCOUNT;
SID *sid;
if (username_sid(username, &sid)) return 0;
const TCHAR *well_known = well_known_sid(sid);
FreeSid(sid);
return well_known;
}
int grant_logon_as_service(const TCHAR *username) {
if (! username) return 0;
/* Open Policy object. */
LSA_OBJECT_ATTRIBUTES attributes;
ZeroMemory(&attributes, sizeof(attributes));
LSA_HANDLE policy;
NTSTATUS status;
if (open_lsa_policy(&policy)) return 1;
/* Look up SID for the account. */
SID *sid;
if (username_sid(username, &sid, &policy)) {
LsaClose(policy);
return 2;
}
/*
Shouldn't happen because it should have been checked before callling this function.
*/
if (well_known_sid(sid)) {
LsaClose(policy);
return 3;
}
/* Check if the SID has the "Log on as a service" right. */
LSA_UNICODE_STRING lsa_right;
lsa_right.Buffer = NSSM_LOGON_AS_SERVICE_RIGHT;
lsa_right.Length = (unsigned short) wcslen(lsa_right.Buffer) * sizeof(wchar_t);
lsa_right.MaximumLength = lsa_right.Length + sizeof(wchar_t);
LSA_UNICODE_STRING *rights;
unsigned long count = ~0;
status = LsaEnumerateAccountRights(policy, sid, &rights, &count);
if (status) {
/*
If the account has no rights set LsaEnumerateAccountRights() will return
STATUS_OBJECT_NAME_NOT_FOUND and set count to 0.
*/
unsigned long error = LsaNtStatusToWinError(status);
if (error != ERROR_FILE_NOT_FOUND) {
FreeSid(sid);
LsaClose(policy);
print_message(stderr, NSSM_MESSAGE_LSAENUMERATEACCOUNTRIGHTS_FAILED, username, error_string(error));
return 4;
}
}
for (unsigned long i = 0; i < count; i++) {
if (rights[i].Length != lsa_right.Length) continue;
if (_wcsnicmp(rights[i].Buffer, lsa_right.Buffer, lsa_right.MaximumLength)) continue;
/* The SID has the right. */
FreeSid(sid);
LsaFreeMemory(rights);
LsaClose(policy);
return 0;
}
LsaFreeMemory(rights);
/* Add the right. */
status = LsaAddAccountRights(policy, sid, &lsa_right, 1);
FreeSid(sid);
LsaClose(policy);
if (status) {
print_message(stderr, NSSM_MESSAGE_LSAADDACCOUNTRIGHTS_FAILED, error_string(LsaNtStatusToWinError(status)));
return 5;
}
print_message(stdout, NSSM_MESSAGE_GRANTED_LOGON_AS_SERVICE, username);
return 0;
}

@ -0,0 +1,24 @@
#ifndef ACCOUNT_H
#define ACCOUNT_H
#include <ntsecapi.h>
/* Not really an account. The canonical name is NT Authority\System. */
#define NSSM_LOCALSYSTEM_ACCOUNT _T("LocalSystem")
/* Other well-known accounts which can start a service without a password. */
#define NSSM_LOCALSERVICE_ACCOUNT _T("NT Authority\\LocalService")
#define NSSM_NETWORKSERVICE_ACCOUNT _T("NT Authority\\NetworkService")
/* This is explicitly a wide string. */
#define NSSM_LOGON_AS_SERVICE_RIGHT L"SeServiceLogonRight"
int open_lsa_policy(LSA_HANDLE *);
int username_sid(const TCHAR *, SID **, LSA_HANDLE *);
int username_sid(const TCHAR *, SID **);
int username_equiv(const TCHAR *, const TCHAR *);
int canonicalise_username(const TCHAR *, TCHAR **);
int is_localsystem(const TCHAR *);
const TCHAR *well_known_sid(SID *);
const TCHAR *well_known_username(const TCHAR *);
int grant_logon_as_service(const TCHAR *);
#endif

@ -0,0 +1,175 @@
#include "nssm.h"
/* See if we were launched from a console window. */
void check_console() {
/* If we're running in a service context there will be no console window. */
HWND console = GetConsoleWindow();
if (! console) return;
unsigned long pid;
if (! GetWindowThreadProcessId(console, &pid)) return;
/*
If the process associated with the console window handle is the same as
this process, we were not launched from an existing console. The user
probably double-clicked our executable.
*/
if (GetCurrentProcessId() != pid) return;
/* We close our new console so that subsequent messages appear in a popup. */
FreeConsole();
}
/* Helpers for drawing the banner. */
static inline void block(unsigned int a, short x, short y, unsigned long n) {
HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
TCHAR s = _T(' ');
unsigned long out;
COORD c = { x, y };
FillConsoleOutputAttribute(h, a, n, c, &out);
FillConsoleOutputCharacter(h, s, n, c, &out);
}
static inline void R(short x, short y, unsigned long n) {
block(BACKGROUND_RED | BACKGROUND_INTENSITY, x, y, n);
}
static inline void r(short x, short y, unsigned long n) {
block(BACKGROUND_RED, x, y, n);
}
static inline void b(short x, short y, unsigned long n) {
block(0, x, y, n);
}
void alloc_console(nssm_service_t *service) {
if (service->no_console) return;
AllocConsole();
/* Disable accidental closure. */
HWND window = GetConsoleWindow();
HMENU menu = GetSystemMenu(window, false);
EnableMenuItem(menu, SC_CLOSE, MF_GRAYED);
/* Set a title like "[NSSM] Jenkins" */
TCHAR displayname[SERVICE_NAME_LENGTH];
unsigned long len = _countof(displayname);
SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT);
if (services) {
if (! GetServiceDisplayName(services, service->name, displayname, &len)) ZeroMemory(displayname, sizeof(displayname));
CloseServiceHandle(services);
}
if (! displayname[0]) _sntprintf_s(displayname, _countof(displayname), _TRUNCATE, _T("%s"), service->name);
TCHAR title[65535];
_sntprintf_s(title, _countof(title), _TRUNCATE, _T("[%s] %s"), NSSM, displayname);
SetConsoleTitle(title);
/* Draw the NSSM logo on the console window. */
short y = 0;
b(0, y, 80);
y++;
b(0, y, 80);
y++;
b(0, y, 80);
y++;
b(0, y, 80);
y++;
b(0, y, 80);
r(18, y, 5); r(28, y, 4); r(41, y, 4); r(68, y, 1);
R(6, y, 5); R(19, y, 4); R(29, y, 1); R(32, y, 3); R(42, y, 1); R(45, y, 3); R(52, y, 5); R(69, y, 4);
y++;
b(0, y, 80);
r(8, y, 4); r(20, y, 1); r(28, y, 1); r(33, y, 3); r(41, y, 1); r(46, y, 3); r (57, y, 1);
R(9, y, 2); R(21, y, 1); R(27, y, 1); R(34, y, 1); R(40, y, 1); R(47, y, 1); R(54, y, 3); R(68, y, 3);
y++;
b(0, y, 80);
r(12, y, 1); r(20, y, 1); r(26, y, 1); r(34, y, 2); r(39, y, 1); r(47, y, 2); r(67, y, 2);
R(9, y, 3); R(21, y, 1); R(27, y, 1); R(40, y, 1); R(54, y, 1); R(56, y, 2); R(67, y, 1); R(69, y, 2);
y++;
b(0, y, 80);
r(9, y, 1); r(20, y, 1); r(26, y, 1); r (35, y, 1); r(39, y, 1); r(48, y, 1); r(58, y, 1);
R(10, y, 3); R(21, y, 1); R(27, y, 1); R(40, y, 1); R(54, y, 1); R(56, y, 2); R(67, y, 1); R(69, y, 2);
y++;
b(0, y, 80);
r(9, y, 1); r(56, y, 1); r(66, y, 2);
R(11, y, 3); R(21, y, 1); R(26, y, 2); R(39, y, 2); R(54, y, 1); R(57, y, 2); R(69, y, 2);
y++;
b(0, y, 80);
r(9, y, 1); r(26, y, 1); r(39, y, 1); r(59, y, 1);
R(12, y, 3); R(21, y, 1); R(27, y, 2); R(40, y, 2); R(54, y, 1); R(57, y, 2); R(66, y, 1); R(69, y, 2);
y++;
b(0, y, 80);
r(9, y, 1); r(12, y, 4); r(30, y, 1); r(43, y, 1); r(57, y, 1); r(65, y, 2);
R(13, y, 2); R(21, y, 1); R(27, y, 3); R(40, y, 3); R(54, y, 1); R(58, y, 2); R(69, y, 2);
y++;
b(0, y, 80);
r(9, y, 1); r(13, y, 4); r(27, y, 7); r(40, y, 7);
R(14, y, 2); R(21, y, 1); R(28, y, 5); R(41, y, 5); R(54, y, 1); R(58, y, 2); R(65, y, 1); R(69, y, 2);
y++;
b(0, y, 80);
r(9, y, 1); r(60, y, 1); r(65, y, 1);
R(14, y, 3); R(21, y, 1); R(29, y, 6); R(42, y, 6); R(54, y, 1); R(58, y, 2); R(69, y, 2);
y++;
b(0, y, 80);
r(9, y, 1); r(31, y, 1); r(44, y, 1); r(58, y, 1); r(64, y, 1);
R(15, y, 3); R(21, y, 1); R(32, y, 4); R(45, y, 4); R(54, y, 1); R(59, y, 2); R(69, y, 2);
y++;
b(0, y, 80);
r(9, y, 1); r(33, y, 1); r(46, y, 1); r(61, y, 1); r(64, y, 1);
R(16, y, 3); R(21, y, 1); R(34, y, 2); R(47, y, 2); R(54, y, 1); R(59, y, 2); R(69, y, 2);
y++;
b(0, y, 80);
r(9, y, 1); r(16, y, 4); r(36, y, 1); r(49, y, 1); r(59, y, 1); r(63, y, 1);
R(17, y, 2); R(21, y, 1); R(34, y, 2); R(47, y, 2); R(54, y, 1); R(60, y, 2); R(69, y, 2);
y++;
b(0, y, 80);
r(9, y, 1); r(17, y, 4); r(26, y, 1); r(36, y, 1); r(39, y, 1); r(49, y, 1);
R(18, y, 2); R(21, y, 1); R(35, y, 1); R(48, y, 1); R(54, y, 1); R(60, y, 2); R(63, y, 1); R(69, y, 2);
y++;
b(0, y, 80);
r(26, y, 2); r(39, y, 2); r(63, y, 1);
R(9, y, 1); R(18, y, 4); R(35, y, 1); R(48, y, 1); R(54, y, 1); R(60, y, 3); R(69, y, 2);
y++;
b(0, y, 80);
r(34, y, 1); r(47, y, 1); r(60, y, 1);
R(9, y, 1); R(19, y, 3); R(26, y, 2); R(35, y, 1); R(39, y, 2); R(48, y, 1); R(54, y, 1); R(61, y, 2); R(69, y, 2);
y++;
b(0, y, 80);
r(8, y, 1); r(35, y, 1); r(48, y, 1); r(62, y, 1); r(71, y, 1);
R(9, y, 1); R(20, y, 2); R(26, y, 3); R(34, y, 1); R(39, y, 3); R(47, y, 1); R(54, y, 1); R(61, y, 1); R(69, y, 2);
y++;
b(0, y, 80);
r(11, y, 1); r(26, y, 1); r(28, y, 5); r(39, y, 1); r(41, y, 5); r(51, y, 7); r(61, y, 1); r(66, y, 8);
R(7, y, 4); R(21, y, 1); R(29, y, 1); R(33, y, 1); R(42, y, 1); R(46, y, 1); R(52, y, 5); R(67, y, 7);
y++;
b(0, y, 80);
y++;
b(0, y, 80);
y++;
}

@ -0,0 +1,7 @@
#ifndef CONSOLE_H
#define CONSOLE_H
void check_console();
void alloc_console(nssm_service_t *);
#endif

@ -0,0 +1,173 @@
#include "nssm.h"
/* Copy an environment block. */
TCHAR *copy_environment_block(TCHAR *env) {
unsigned long len;
if (! env) return 0;
for (len = 0; env[len]; len++) while (env[len]) len++;
if (! len++) return 0;
TCHAR *newenv = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR));
if (! newenv) {
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("environment"), _T("copy_environment_block()"), 0);
return 0;
}
memmove(newenv, env, len * sizeof(TCHAR));
return newenv;
}
/*
The environment block starts with variables of the form
=C:=C:\Windows\System32 which we ignore.
*/
TCHAR *useful_environment(TCHAR *rawenv) {
TCHAR *env = rawenv;
if (env) {
while (*env == _T('=')) {
for ( ; *env; env++);
env++;
}
}
return env;
}
/* Expand an environment variable. Must call HeapFree() on the result. */
TCHAR *expand_environment_string(TCHAR *string) {
unsigned long len;
len = ExpandEnvironmentStrings(string, 0, 0);
if (! len) {
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_EXPANDENVIRONMENTSTRINGS_FAILED, string, error_string(GetLastError()), 0);
return 0;
}
TCHAR *ret = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR));
if (! ret) {
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("ExpandEnvironmentStrings()"), _T("expand_environment_string"), 0);
return 0;
}
if (! ExpandEnvironmentStrings(string, ret, len)) {
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_EXPANDENVIRONMENTSTRINGS_FAILED, string, error_string(GetLastError()), 0);
HeapFree(GetProcessHeap(), 0, ret);
return 0;
}
return ret;
}
/*
Set all the environment variables from an environment block in the current
environment or remove all the variables in the block from the current
environment.
*/
static int set_environment_block(TCHAR *env, bool set) {
int ret = 0;
TCHAR *s, *t;
for (s = env; *s; s++) {
for (t = s; *t && *t != _T('='); t++);
if (*t == _T('=')) {
*t = _T('\0');
if (set) {
TCHAR *expanded = expand_environment_string(++t);
if (expanded) {
if (! SetEnvironmentVariable(s, expanded)) ret++;
HeapFree(GetProcessHeap(), 0, expanded);
}
else {
if (! SetEnvironmentVariable(s, t)) ret++;
}
}
else {
if (! SetEnvironmentVariable(s, NULL)) ret++;
}
for (t++ ; *t; t++);
}
s = t;
}
return ret;
}
int set_environment_block(TCHAR *env) {
return set_environment_block(env, true);
}
static int unset_environment_block(TCHAR *env) {
return set_environment_block(env, false);
}
/* Remove all variables from the process environment. */
int clear_environment() {
TCHAR *rawenv = GetEnvironmentStrings();
TCHAR *env = useful_environment(rawenv);
int ret = unset_environment_block(env);
if (rawenv) FreeEnvironmentStrings(rawenv);
return ret;
}
/* Set the current environment to exactly duplicate an environment block. */
int duplicate_environment(TCHAR *rawenv) {
int ret = clear_environment();
TCHAR *env = useful_environment(rawenv);
ret += set_environment_block(env);
return ret;
}
/*
Verify an environment block.
Returns: 1 if environment is invalid.
0 if environment is OK.
-1 on error.
*/
int test_environment(TCHAR *env) {
TCHAR path[PATH_LENGTH];
GetModuleFileName(0, path, _countof(path));
STARTUPINFO si;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
PROCESS_INFORMATION pi;
ZeroMemory(&pi, sizeof(pi));
unsigned long flags = CREATE_SUSPENDED;
#ifdef UNICODE
flags |= CREATE_UNICODE_ENVIRONMENT;
#endif
/*
Try to relaunch ourselves but with the candidate environment set.
Assuming no solar flare activity, the only reason this would fail is if
the environment were invalid.
*/
if (CreateProcess(0, path, 0, 0, 0, flags, env, 0, &si, &pi)) {
TerminateProcess(pi.hProcess, 0);
}
else {
unsigned long error = GetLastError();
if (error == ERROR_INVALID_PARAMETER) return 1;
else return -1;
}
return 0;
}
/*
Duplicate an environment block returned by GetEnvironmentStrings().
Since such a block is by definition readonly, and duplicate_environment()
modifies its inputs, this function takes a copy of the input and operates
on that.
*/
void duplicate_environment_strings(TCHAR *env) {
TCHAR *newenv = copy_environment_block(env);
if (! newenv) return;
duplicate_environment(newenv);
HeapFree(GetProcessHeap(), 0, newenv);
}

@ -0,0 +1,13 @@
#ifndef ENV_H
#define ENV_H
TCHAR *copy_environment_block(TCHAR *);
TCHAR *useful_environment(TCHAR *);
TCHAR *expand_environment_string(TCHAR *);
int set_environment_block(TCHAR *);
int clear_environment();
int duplicate_environment(TCHAR *);
int test_environment(TCHAR *);
void duplicate_environment_strings(TCHAR *);
#endif

@ -0,0 +1,111 @@
#include "nssm.h"
#define NSSM_SOURCE _T("nssm")
#define NSSM_ERROR_BUFSIZE 65535
#define NSSM_NUM_EVENT_STRINGS 16
unsigned long tls_index;
/* Convert error code to error string - must call LocalFree() on return value */
TCHAR *error_string(unsigned long error) {
/* Thread-safe buffer */
TCHAR *error_message = (TCHAR *) TlsGetValue(tls_index);
if (! error_message) {
error_message = (TCHAR *) LocalAlloc(LPTR, NSSM_ERROR_BUFSIZE);
if (! error_message) return _T("<out of memory for error message>");
TlsSetValue(tls_index, (void *) error_message);
}
if (! FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0, error, GetUserDefaultLangID(), (TCHAR *) error_message, NSSM_ERROR_BUFSIZE, 0)) {
if (! FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0, error, 0, (TCHAR *) error_message, NSSM_ERROR_BUFSIZE, 0)) {
if (_sntprintf_s(error_message, NSSM_ERROR_BUFSIZE, _TRUNCATE, _T("system error %lu"), error) < 0) return 0;
}
}
return error_message;
}
/* Convert message code to format string */
TCHAR *message_string(unsigned long error) {
TCHAR *ret;
if (! FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, 0, error, GetUserDefaultLangID(), (LPTSTR) &ret, NSSM_ERROR_BUFSIZE, 0)) {
if (! FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS, 0, error, 0, (LPTSTR) &ret, NSSM_ERROR_BUFSIZE, 0)) {
ret = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, 32 * sizeof(TCHAR));
if (_sntprintf_s(ret, NSSM_ERROR_BUFSIZE, _TRUNCATE, _T("system error %lu"), error) < 0) return 0;
}
}
return ret;
}
/* Log a message to the Event Log */
void log_event(unsigned short type, unsigned long id, ...) {
va_list arg;
int count;
TCHAR *s;
TCHAR *strings[NSSM_NUM_EVENT_STRINGS];
/* Open event log */
HANDLE handle = RegisterEventSource(0, NSSM_SOURCE);
if (! handle) return;
/* Log it */
count = 0;
va_start(arg, id);
while ((s = va_arg(arg, TCHAR *)) && count < NSSM_NUM_EVENT_STRINGS - 1) strings[count++] = s;
strings[count] = 0;
va_end(arg);
ReportEvent(handle, type, 0, id, 0, count, 0, (const TCHAR **) strings, 0);
/* Close event log */
DeregisterEventSource(handle);
}
/* Log a message to the console */
void print_message(FILE *file, unsigned long id, ...) {
va_list arg;
TCHAR *format = message_string(id);
if (! format) return;
va_start(arg, id);
_vftprintf(file, format, arg);
va_end(arg);
LocalFree(format);
}
/* Show a GUI dialogue */
int popup_message(HWND owner, unsigned int type, unsigned long id, ...) {
va_list arg;
TCHAR *format = message_string(id);
if (! format) {
return MessageBox(0, _T("The message which was supposed to go here is missing!"), NSSM, MB_OK | MB_ICONEXCLAMATION);
}
TCHAR blurb[NSSM_ERROR_BUFSIZE];
va_start(arg, id);
if (_vsntprintf_s(blurb, _countof(blurb), _TRUNCATE, format, arg) < 0) {
va_end(arg);
LocalFree(format);
return MessageBox(0, _T("The message which was supposed to go here is too big!"), NSSM, MB_OK | MB_ICONEXCLAMATION);
}
va_end(arg);
MSGBOXPARAMS params;
ZeroMemory(&params, sizeof(params));
params.cbSize = sizeof(params);
params.hInstance = GetModuleHandle(0);
params.hwndOwner = owner;
params.lpszText = blurb;
params.lpszCaption = NSSM;
params.dwStyle = type;
if (type == MB_OK) {
params.dwStyle |= MB_USERICON;
params.lpszIcon = MAKEINTRESOURCE(IDI_NSSM);
}
int ret = MessageBoxIndirect(&params);
LocalFree(format);
return ret;
}

@ -0,0 +1,10 @@
#ifndef EVENT_H
#define EVENT_H
TCHAR *error_string(unsigned long);
TCHAR *message_string(unsigned long);
void log_event(unsigned short, unsigned long, ...);
void print_message(FILE *, unsigned long, ...);
int popup_message(HWND, unsigned int, unsigned long, ...);
#endif

File diff suppressed because it is too large Load Diff

@ -0,0 +1,18 @@
#ifndef GUI_H
#define GUI_H
#include <stdio.h>
#include <windows.h>
#include <commctrl.h>
#include "resource.h"
int nssm_gui(int, nssm_service_t *);
void centre_window(HWND);
int configure(HWND, nssm_service_t *, nssm_service_t *);
int install(HWND);
int remove(HWND);
int edit(HWND, nssm_service_t *);
void browse(HWND);
INT_PTR CALLBACK nssm_dlg(HWND, UINT, WPARAM, LPARAM);
#endif

@ -0,0 +1,92 @@
#include "nssm.h"
imports_t imports;
/*
Try to set up function pointers.
In this first implementation it is not an error if we can't load them
because we aren't currently trying to load any functions which we
absolutely need. If we later add some indispensible imports we can
return non-zero here to force an application exit.
*/
HMODULE get_dll(const TCHAR *dll, unsigned long *error) {
*error = 0;
HMODULE ret = LoadLibrary(dll);
if (! ret) {
*error = GetLastError();
log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_LOADLIBRARY_FAILED, dll, error_string(*error), 0);
}
return ret;
}
FARPROC get_import(HMODULE library, const char *function, unsigned long *error) {
*error = 0;
FARPROC ret = GetProcAddress(library, function);
if (! ret) {
*error = GetLastError();
TCHAR *function_name;
#ifdef UNICODE
size_t buflen;
mbstowcs_s(&buflen, NULL, 0, function, _TRUNCATE);
function_name = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, buflen * sizeof(TCHAR));
if (function_name) mbstowcs_s(&buflen, function_name, buflen * sizeof(TCHAR), function, _TRUNCATE);
#else
function_name = (TCHAR *) function;
#endif
if (*error != ERROR_PROC_NOT_FOUND) log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_GETPROCADDRESS_FAILED, function_name, error_string(*error), 0);
#ifdef UNICODE
if (function_name) HeapFree(GetProcessHeap(), 0, function_name);
#endif
}
return ret;
}
int get_imports() {
unsigned long error;
ZeroMemory(&imports, sizeof(imports));
imports.kernel32 = get_dll(_T("kernel32.dll"), &error);
if (imports.kernel32) {
imports.AttachConsole = (AttachConsole_ptr) get_import(imports.kernel32, "AttachConsole", &error);
if (! imports.AttachConsole) {
if (error != ERROR_PROC_NOT_FOUND) return 2;
}
imports.SleepConditionVariableCS = (SleepConditionVariableCS_ptr) get_import(imports.kernel32, "SleepConditionVariableCS", &error);
if (! imports.SleepConditionVariableCS) {
if (error != ERROR_PROC_NOT_FOUND) return 3;
}
imports.WakeConditionVariable = (WakeConditionVariable_ptr) get_import(imports.kernel32, "WakeConditionVariable", &error);
if (! imports.WakeConditionVariable) {
if (error != ERROR_PROC_NOT_FOUND) return 4;
}
}
else if (error != ERROR_MOD_NOT_FOUND) return 1;
imports.advapi32 = get_dll(_T("advapi32.dll"), &error);
if (imports.advapi32) {
imports.CreateWellKnownSid = (CreateWellKnownSid_ptr) get_import(imports.advapi32, "CreateWellKnownSid", &error);
if (! imports.CreateWellKnownSid) {
if (error != ERROR_PROC_NOT_FOUND) return 6;
}
imports.IsWellKnownSid = (IsWellKnownSid_ptr) get_import(imports.advapi32, "IsWellKnownSid", &error);
if (! imports.IsWellKnownSid) {
if (error != ERROR_PROC_NOT_FOUND) return 7;
}
}
else if (error != ERROR_MOD_NOT_FOUND) return 5;
return 0;
}
void free_imports() {
if (imports.kernel32) FreeLibrary(imports.kernel32);
if (imports.advapi32) FreeLibrary(imports.advapi32);
ZeroMemory(&imports, sizeof(imports));
}

@ -0,0 +1,25 @@
#ifndef IMPORTS_H
#define IMPORTS_H
typedef BOOL (WINAPI *AttachConsole_ptr)(DWORD);
typedef BOOL (WINAPI *SleepConditionVariableCS_ptr)(PCONDITION_VARIABLE, PCRITICAL_SECTION, DWORD);
typedef void (WINAPI *WakeConditionVariable_ptr)(PCONDITION_VARIABLE);
typedef BOOL (WINAPI *CreateWellKnownSid_ptr)(WELL_KNOWN_SID_TYPE, SID *, SID *, unsigned long *);
typedef BOOL (WINAPI *IsWellKnownSid_ptr)(SID *, WELL_KNOWN_SID_TYPE);
typedef struct {
HMODULE kernel32;
HMODULE advapi32;
AttachConsole_ptr AttachConsole;
SleepConditionVariableCS_ptr SleepConditionVariableCS;
WakeConditionVariable_ptr WakeConditionVariable;
CreateWellKnownSid_ptr CreateWellKnownSid;
IsWellKnownSid_ptr IsWellKnownSid;
} imports_t;
HMODULE get_dll(const TCHAR *, unsigned long *);
FARPROC get_import(HMODULE, const char *, unsigned long *);
int get_imports();
void free_imports();
#endif

@ -0,0 +1,560 @@
#include "nssm.h"
#define COMPLAINED_READ (1 << 0)
#define COMPLAINED_WRITE (1 << 1)
#define COMPLAINED_ROTATE (1 << 2)
static HANDLE create_logging_thread(TCHAR *service_name, TCHAR *path, unsigned long sharing, unsigned long disposition, unsigned long flags, HANDLE *read_handle_ptr, HANDLE *pipe_handle_ptr, HANDLE *write_handle_ptr, unsigned long rotate_bytes_low, unsigned long rotate_bytes_high, unsigned long *tid_ptr, unsigned long *rotate_online) {
*tid_ptr = 0;
/* Pipe between application's stdout/stderr and our logging handle. */
if (read_handle_ptr && ! *read_handle_ptr) {
if (pipe_handle_ptr && ! *pipe_handle_ptr) {
if (CreatePipe(read_handle_ptr, pipe_handle_ptr, 0, 0)) {
SetHandleInformation(*pipe_handle_ptr, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
}
else {
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPIPE_FAILED, service_name, path, error_string(GetLastError()));
return (HANDLE) 0;
}
}
}
logger_t *logger = (logger_t *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(logger_t));
if (! logger) {
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("logger"), _T("create_logging_thread()"), 0);
return (HANDLE) 0;
}
ULARGE_INTEGER size;
size.LowPart = rotate_bytes_low;
size.HighPart = rotate_bytes_high;
logger->service_name = service_name;
logger->path = path;
logger->sharing = sharing;
logger->disposition = disposition;
logger->flags = flags;
logger->read_handle = *read_handle_ptr;
logger->write_handle = *write_handle_ptr;
logger->size = (__int64) size.QuadPart;
logger->tid_ptr = tid_ptr;
logger->rotate_online = rotate_online;
HANDLE thread_handle = CreateThread(NULL, 0, log_and_rotate, (void *) logger, 0, logger->tid_ptr);
if (! thread_handle) {
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);
HeapFree(GetProcessHeap(), 0, logger);
}
return thread_handle;
}
static inline unsigned long guess_charsize(void *address, unsigned long bufsize) {
if (IsTextUnicode(address, bufsize, 0)) return (unsigned long) sizeof(wchar_t);
else return (unsigned long) sizeof(char);
}
static inline void write_bom(logger_t *logger, unsigned long *out) {
wchar_t bom = L'\ufeff';
if (! WriteFile(logger->write_handle, (void *) &bom, sizeof(bom), out, 0)) {
log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_SOMEBODY_SET_UP_US_THE_BOM, logger->service_name, logger->path, error_string(GetLastError()), 0);
}
}
/* Get path, share mode, creation disposition and flags for a stream. */
int get_createfile_parameters(HKEY key, TCHAR *prefix, TCHAR *path, unsigned long *sharing, unsigned long default_sharing, unsigned long *disposition, unsigned long default_disposition, unsigned long *flags, unsigned long default_flags) {
TCHAR value[NSSM_STDIO_LENGTH];
/* Path. */
if (_sntprintf_s(value, _countof(value), _TRUNCATE, _T("%s"), prefix) < 0) {
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, prefix, _T("get_createfile_parameters()"), 0);
return 1;
}
switch (expand_parameter(key, value, path, PATH_LENGTH, true, false)) {
case 0: if (! path[0]) return 0; break; /* OK. */
default: return 2; /* Error. */
}
/* ShareMode. */
if (_sntprintf_s(value, _countof(value), _TRUNCATE, _T("%s%s"), prefix, NSSM_REG_STDIO_SHARING) < 0) {
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, NSSM_REG_STDIO_SHARING, _T("get_createfile_parameters()"), 0);
return 3;
}
switch (get_number(key, value, sharing, false)) {
case 0: *sharing = default_sharing; break; /* Missing. */
case 1: break; /* Found. */
case -2: return 4; break; /* Error. */
}
/* CreationDisposition. */
if (_sntprintf_s(value, _countof(value), _TRUNCATE, _T("%s%s"), prefix, NSSM_REG_STDIO_DISPOSITION) < 0) {
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, NSSM_REG_STDIO_DISPOSITION, _T("get_createfile_parameters()"), 0);
return 5;
}
switch (get_number(key, value, disposition, false)) {
case 0: *disposition = default_disposition; break; /* Missing. */
case 1: break; /* Found. */
case -2: return 6; break; /* Error. */
}
/* Flags. */
if (_sntprintf_s(value, _countof(value), _TRUNCATE, _T("%s%s"), prefix, NSSM_REG_STDIO_FLAGS) < 0) {
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, NSSM_REG_STDIO_FLAGS, _T("get_createfile_parameters()"), 0);
return 7;
}
switch (get_number(key, value, flags, false)) {
case 0: *flags = default_flags; break; /* Missing. */
case 1: break; /* Found. */
case -2: return 8; break; /* Error. */
}
return 0;
}
int set_createfile_parameter(HKEY key, TCHAR *prefix, TCHAR *suffix, unsigned long number) {
TCHAR value[NSSM_STDIO_LENGTH];
if (_sntprintf_s(value, _countof(value), _TRUNCATE, _T("%s%s"), prefix, suffix) < 0) {
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, suffix, _T("set_createfile_parameter()"), 0);
return 1;
}
return set_number(key, value, number);
}
int delete_createfile_parameter(HKEY key, TCHAR *prefix, TCHAR *suffix) {
TCHAR value[NSSM_STDIO_LENGTH];
if (_sntprintf_s(value, _countof(value), _TRUNCATE, _T("%s%s"), prefix, suffix) < 0) {
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, suffix, _T("delete_createfile_parameter()"), 0);
return 1;
}
if (RegDeleteValue(key, value)) return 0;
return 1;
}
HANDLE write_to_file(TCHAR *path, unsigned long sharing, SECURITY_ATTRIBUTES *attributes, unsigned long disposition, unsigned long flags) {
HANDLE ret = CreateFile(path, FILE_WRITE_DATA, sharing, attributes, disposition, flags, 0);
if (ret) {
if (SetFilePointer(ret, 0, 0, FILE_END) != INVALID_SET_FILE_POINTER) SetEndOfFile(ret);
return ret;
}
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEFILE_FAILED, path, error_string(GetLastError()), 0);
return ret;
}
static void rotated_filename(TCHAR *path, TCHAR *rotated, unsigned long rotated_len, SYSTEMTIME *st) {
if (! st) {
SYSTEMTIME now;
st = &now;
GetSystemTime(st);
}
TCHAR buffer[PATH_LENGTH];
memmove(buffer, path, sizeof(buffer));
TCHAR *ext = PathFindExtension(buffer);
TCHAR extension[PATH_LENGTH];
_sntprintf_s(extension, _countof(extension), _TRUNCATE, _T("-%04u%02u%02uT%02u%02u%02u.%03u%s"), st->wYear, st->wMonth, st->wDay, st->wHour, st->wMinute, st->wSecond, st->wMilliseconds, ext);
*ext = _T('\0');
_sntprintf_s(rotated, rotated_len, _TRUNCATE, _T("%s%s"), buffer, extension);
}
void rotate_file(TCHAR *service_name, TCHAR *path, unsigned long seconds, unsigned long low, unsigned long high) {
unsigned long error;
/* Now. */
SYSTEMTIME st;
GetSystemTime(&st);
BY_HANDLE_FILE_INFORMATION info;
/* Try to open the file to check if it exists and to get attributes. */
HANDLE file = CreateFile(path, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if (file) {
/* Get file attributes. */
if (! GetFileInformationByHandle(file, &info)) {
/* Reuse current time for rotation timestamp. */
seconds = low = high = 0;
SystemTimeToFileTime(&st, &info.ftLastWriteTime);
}
CloseHandle(file);
}
else {
error = GetLastError();
if (error == ERROR_FILE_NOT_FOUND) return;
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_ROTATE_FILE_FAILED, service_name, path, _T("CreateFile()"), path, error_string(error), 0);
/* Reuse current time for rotation timestamp. */
seconds = low = high = 0;
SystemTimeToFileTime(&st, &info.ftLastWriteTime);
}
/* Check file age. */
if (seconds) {
FILETIME ft;
SystemTimeToFileTime(&st, &ft);
ULARGE_INTEGER s;
s.LowPart = ft.dwLowDateTime;
s.HighPart = ft.dwHighDateTime;
s.QuadPart -= seconds * 10000000LL;
ft.dwLowDateTime = s.LowPart;
ft.dwHighDateTime = s.HighPart;
if (CompareFileTime(&info.ftLastWriteTime, &ft) > 0) return;
}
/* Check file size. */
if (low || high) {
if (info.nFileSizeHigh < high) return;
if (info.nFileSizeHigh == high && info.nFileSizeLow < low) return;
}
/* Get new filename. */
FileTimeToSystemTime(&info.ftLastWriteTime, &st);
TCHAR rotated[PATH_LENGTH];
rotated_filename(path, rotated, _countof(rotated), &st);
/* Rotate. */
if (MoveFile(path, rotated)) {
log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ROTATED, service_name, path, rotated, 0);
return;
}
error = GetLastError();
if (error == ERROR_FILE_NOT_FOUND) return;
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_ROTATE_FILE_FAILED, service_name, path, _T("MoveFile()"), rotated, error_string(error), 0);
return;
}
int get_output_handles(nssm_service_t *service, STARTUPINFO *si) {
if (! si) return 1;
/* Allocate a new console so we get a fresh stdin, stdout and stderr. */
alloc_console(service);
/* stdin */
if (service->stdin_path[0]) {
si->hStdInput = CreateFile(service->stdin_path, FILE_READ_DATA, service->stdin_sharing, 0, service->stdin_disposition, service->stdin_flags, 0);
if (! si->hStdInput) {
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEFILE_FAILED, service->stdin_path, error_string(GetLastError()), 0);
return 2;
}
}
/* stdout */
if (service->stdout_path[0]) {
if (service->rotate_files) rotate_file(service->name, service->stdout_path, service->rotate_seconds, service->rotate_bytes_low, service->rotate_bytes_high);
HANDLE stdout_handle = write_to_file(service->stdout_path, service->stdout_sharing, 0, service->stdout_disposition, service->stdout_flags);
if (! stdout_handle) return 4;
if (service->rotate_files && service->rotate_stdout_online) {
service->stdout_pipe = si->hStdOutput = 0;
service->stdout_thread = create_logging_thread(service->name, service->stdout_path, service->stdout_sharing, service->stdout_disposition, service->stdout_flags, &service->stdout_pipe, &si->hStdOutput, &stdout_handle, service->rotate_bytes_low, service->rotate_bytes_high, &service->stdout_tid, &service->rotate_stdout_online);
if (! service->stdout_thread) {
CloseHandle(service->stdout_pipe);
CloseHandle(si->hStdOutput);
}
}
else service->stdout_thread = 0;
if (! service->stdout_thread) {
if (! DuplicateHandle(GetCurrentProcess(), stdout_handle, GetCurrentProcess(), &si->hStdOutput, 0, true, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_DUPLICATEHANDLE_FAILED, NSSM_REG_STDOUT, _T("stdout"), error_string(GetLastError()), 0);
return 4;
}
service->rotate_stdout_online = NSSM_ROTATE_OFFLINE;
}
}
/* stderr */
if (service->stderr_path[0]) {
/* Same as stdout? */
if (str_equiv(service->stderr_path, service->stdout_path)) {
service->stderr_sharing = service->stdout_sharing;
service->stderr_disposition = service->stdout_disposition;
service->stderr_flags = service->stdout_flags;
service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
/* Two handles to the same file will create a race. */
if (! DuplicateHandle(GetCurrentProcess(), si->hStdOutput, GetCurrentProcess(), &si->hStdError, 0, true, DUPLICATE_SAME_ACCESS)) {
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_DUPLICATEHANDLE_FAILED, NSSM_REG_STDOUT, _T("stderr"), error_string(GetLastError()), 0);
return 6;
}
}
else {
if (service->rotate_files) rotate_file(service->name, service->stderr_path, service->rotate_seconds, service->rotate_bytes_low, service->rotate_bytes_high);
HANDLE stderr_handle = write_to_file(service->stderr_path, service->stderr_sharing, 0, service->stderr_disposition, service->stderr_flags);
if (! stderr_handle) return 7;
if (service->rotate_files && service->rotate_stderr_online) {
service->stderr_pipe = si->hStdError = 0;
service->stderr_thread = create_logging_thread(service->name, service->stderr_path, service->stderr_sharing, service->stderr_disposition, service->stderr_flags, &service->stderr_pipe, &si->hStdError, &stderr_handle, service->rotate_bytes_low, service->rotate_bytes_high, &service->stderr_tid, &service->rotate_stderr_online);
if (! service->stderr_thread) {
CloseHandle(service->stderr_pipe);
CloseHandle(si->hStdError);
}
}
else service->stderr_thread = 0;
if (! service->stderr_thread) {
if (! DuplicateHandle(GetCurrentProcess(), stderr_handle, GetCurrentProcess(), &si->hStdError, 0, true, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_DUPLICATEHANDLE_FAILED, NSSM_REG_STDERR, _T("stderr"), error_string(GetLastError()), 0);
return 7;
}
service->rotate_stderr_online = NSSM_ROTATE_OFFLINE;
}
}
}
/*
We need to set the startup_info flags to make the new handles
inheritable by the new process.
*/
si->dwFlags |= STARTF_USESTDHANDLES;
if (service->no_console) return 0;
/* Redirect other handles. */
if (! si->hStdInput) {
if (! DuplicateHandle(GetCurrentProcess(), GetStdHandle(STD_INPUT_HANDLE), GetCurrentProcess(), &si->hStdInput, 0, true, DUPLICATE_SAME_ACCESS)) {
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_DUPLICATEHANDLE_FAILED, _T("STD_INPUT_HANDLE"), _T("stdin"), error_string(GetLastError()), 0);
return 8;
}
}
if (! si->hStdOutput) {
if (! DuplicateHandle(GetCurrentProcess(), GetStdHandle(STD_OUTPUT_HANDLE), GetCurrentProcess(), &si->hStdOutput, 0, true, DUPLICATE_SAME_ACCESS)) {
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_DUPLICATEHANDLE_FAILED, _T("STD_OUTPUT_HANDLE"), _T("stdout"), error_string(GetLastError()), 0);
return 9;
}
}
if (! si->hStdError) {
if (! DuplicateHandle(GetCurrentProcess(), GetStdHandle(STD_ERROR_HANDLE), GetCurrentProcess(), &si->hStdError, 0, true, DUPLICATE_SAME_ACCESS)) {
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_DUPLICATEHANDLE_FAILED, _T("STD_ERROR_HANDLE"), _T("stderr"), error_string(GetLastError()), 0);
return 10;
}
}
return 0;
}
void close_output_handles(STARTUPINFO *si) {
if (si->hStdInput) CloseHandle(si->hStdInput);
if (si->hStdOutput) CloseHandle(si->hStdOutput);
if (si->hStdError) CloseHandle(si->hStdError);
}
/*
Try multiple times to read from a file.
Returns: 0 on success.
1 on non-fatal error.
-1 on fatal error.
*/
static int try_read(logger_t *logger, void *address, unsigned long bufsize, unsigned long *in, int *complained) {
int ret = 1;
unsigned long error;
for (int tries = 0; tries < 5; tries++) {
if (ReadFile(logger->read_handle, address, bufsize, in, 0)) return 0;
error = GetLastError();
switch (error) {
/* Other end closed the pipe. */
case ERROR_BROKEN_PIPE:
ret = -1;
goto complain_read;
/* Couldn't lock the buffer. */
case ERROR_NOT_ENOUGH_QUOTA:
Sleep(2000 + tries * 3000);
ret = 1;
continue;
/* Write was cancelled by the other end. */
case ERROR_OPERATION_ABORTED:
ret = 1;
goto complain_read;
default:
ret = -1;
}
}
complain_read:
/* Ignore the error if we've been requested to exit anyway. */
if (*logger->rotate_online != NSSM_ROTATE_ONLINE) return ret;
if (! (*complained & COMPLAINED_READ)) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_READFILE_FAILED, logger->service_name, logger->path, error_string(error), 0);
*complained |= COMPLAINED_READ;
return ret;
}
/*
Try multiple times to write to a file.
Returns: 0 on success.
1 on non-fatal error.
-1 on fatal error.
*/
static int try_write(logger_t *logger, void *address, unsigned long bufsize, unsigned long *out, int *complained) {
int ret = 1;
unsigned long error;
for (int tries = 0; tries < 5; tries++) {
if (WriteFile(logger->write_handle, address, bufsize, out, 0)) return 0;
error = GetLastError();
if (error == ERROR_IO_PENDING) {
/* Operation was successful pending flush to disk. */
return 0;
}
switch (error) {
/* Other end closed the pipe. */
case ERROR_BROKEN_PIPE:
ret = -1;
goto complain_write;
/* Couldn't lock the buffer. */
case ERROR_NOT_ENOUGH_QUOTA:
/* Out of disk space. */
case ERROR_DISK_FULL:
Sleep(2000 + tries * 3000);
ret = 1;
continue;
default:
/* We'll lose this line but try to read and write subsequent ones. */
ret = 1;
}
}
complain_write:
if (! (*complained & COMPLAINED_WRITE)) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_WRITEFILE_FAILED, logger->service_name, logger->path, error_string(error), 0);
*complained |= COMPLAINED_WRITE;
return ret;
}
/* Wrapper to be called in a new thread for logging. */
unsigned long WINAPI log_and_rotate(void *arg) {
logger_t *logger = (logger_t *) arg;
if (! logger) return 1;
__int64 size;
BY_HANDLE_FILE_INFORMATION info;
/* Find initial file size. */
if (! GetFileInformationByHandle(logger->write_handle, &info)) logger->size = 0LL;
else {
ULARGE_INTEGER l;
l.HighPart = info.nFileSizeHigh;
l.LowPart = info.nFileSizeLow;
size = l.QuadPart;
}
char buffer[1024];
void *address;
unsigned long in, out;
unsigned long charsize = 0;
unsigned long error;
int ret;
int complained = 0;
while (true) {
/* Read data from the pipe. */
address = &buffer;
ret = try_read(logger, address, sizeof(buffer), &in, &complained);
if (ret < 0) {
CloseHandle(logger->read_handle);
CloseHandle(logger->write_handle);
HeapFree(GetProcessHeap(), 0, logger);
return 2;
}
else if (ret) continue;
if (*logger->rotate_online == NSSM_ROTATE_ONLINE_ASAP || (logger->size && size + (__int64) in >= logger->size)) {
/* Look for newline. */
unsigned long i;
for (i = 0; i < in; i++) {
if (buffer[i] == '\n') {
if (! charsize) charsize = guess_charsize(address, in);
i += charsize;
/* Write up to the newline. */
ret = try_write(logger, address, i, &out, &complained);
if (ret < 0) {
HeapFree(GetProcessHeap(), 0, logger);
CloseHandle(logger->read_handle);
CloseHandle(logger->write_handle);
return 3;
}
size += (__int64) out;
/* Rotate. */
*logger->rotate_online = NSSM_ROTATE_ONLINE;
TCHAR rotated[PATH_LENGTH];
rotated_filename(logger->path, rotated, _countof(rotated), 0);
/*
Ideally we'd try the rename first then close the handle but
MoveFile() will fail if the handle is still open so we must
risk losing everything.
*/
CloseHandle(logger->write_handle);
if (MoveFile(logger->path, rotated)) {
log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ROTATED, logger->service_name, logger->path, rotated, 0);
size = 0LL;
}
else {
error = GetLastError();
if (error != ERROR_FILE_NOT_FOUND) {
if (! (complained & COMPLAINED_ROTATE)) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_ROTATE_FILE_FAILED, logger->service_name, logger->path, _T("MoveFile()"), rotated, error_string(error), 0);
complained |= COMPLAINED_ROTATE;
/* We can at least try to re-open the existing file. */
logger->disposition = OPEN_ALWAYS;
}
}
/* Reopen. */
logger->write_handle = write_to_file(logger->path, logger->sharing, 0, logger->disposition, logger->flags);
if (! logger->write_handle) {
error = GetLastError();
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEFILE_FAILED, logger->path, error_string(error), 0);
/* Oh dear. Now we can't log anything further. */
HeapFree(GetProcessHeap(), 0, logger);
CloseHandle(logger->read_handle);
CloseHandle(logger->write_handle);
return 4;
}
/* Resume writing after the newline. */
address = (void *) ((char *) address + i);
in -= i;
}
}
}
if (! size) {
/* Write a BOM to the new file. */
if (! charsize) charsize = guess_charsize(address, in);
if (charsize == sizeof(wchar_t)) write_bom(logger, &out);
size += (__int64) out;
}
/* Write the data, if any. */
if (! in) continue;
ret = try_write(logger, address, in, &out, &complained);
size += (__int64) out;
if (ret < 0) {
HeapFree(GetProcessHeap(), 0, logger);
CloseHandle(logger->read_handle);
CloseHandle(logger->write_handle);
return 3;
}
}
HeapFree(GetProcessHeap(), 0, logger);
CloseHandle(logger->read_handle);
CloseHandle(logger->write_handle);
return 0;
}

@ -0,0 +1,36 @@
#ifndef IO_H
#define IO_H
#define NSSM_STDIN_SHARING FILE_SHARE_WRITE
#define NSSM_STDIN_DISPOSITION OPEN_EXISTING
#define NSSM_STDIN_FLAGS FILE_ATTRIBUTE_NORMAL
#define NSSM_STDOUT_SHARING (FILE_SHARE_READ | FILE_SHARE_WRITE)
#define NSSM_STDOUT_DISPOSITION OPEN_ALWAYS
#define NSSM_STDOUT_FLAGS FILE_ATTRIBUTE_NORMAL
#define NSSM_STDERR_SHARING (FILE_SHARE_READ | FILE_SHARE_WRITE)
#define NSSM_STDERR_DISPOSITION OPEN_ALWAYS
#define NSSM_STDERR_FLAGS FILE_ATTRIBUTE_NORMAL
typedef struct {
TCHAR *service_name;
TCHAR *path;
unsigned long sharing;
unsigned long disposition;
unsigned long flags;
HANDLE read_handle;
HANDLE write_handle;
__int64 size;
unsigned long *tid_ptr;
unsigned long *rotate_online;
} logger_t;
int get_createfile_parameters(HKEY, TCHAR *, TCHAR *, unsigned long *, unsigned long, unsigned long *, unsigned long, unsigned long *, unsigned long);
int set_createfile_parameter(HKEY, TCHAR *, TCHAR *, unsigned long);
int delete_createfile_parameter(HKEY, TCHAR *, TCHAR *);
HANDLE write_to_file(TCHAR *, unsigned long, SECURITY_ATTRIBUTES *, unsigned long, unsigned long);
void rotate_file(TCHAR *, TCHAR *, unsigned long, unsigned long, unsigned long);
int get_output_handles(nssm_service_t *, STARTUPINFO *);
void close_output_handles(STARTUPINFO *);
unsigned long WINAPI log_and_rotate(void *);
#endif

@ -0,0 +1,187 @@
#include "nssm.h"
extern unsigned long tls_index;
extern bool is_admin;
extern imports_t imports;
/* Are two strings case-insensitively equivalent? */
int str_equiv(const TCHAR *a, const TCHAR *b) {
size_t len = _tcslen(a);
if (_tcslen(b) != len) return 0;
if (_tcsnicmp(a, b, len)) return 0;
return 1;
}
/* Convert a string to a number. */
int str_number(const TCHAR *string, unsigned long *number, TCHAR **bogus) {
if (! string) return 1;
*number = _tcstoul(string, bogus, 0);
if (**bogus) return 2;
return 0;
}
int str_number(const TCHAR *string, unsigned long *number) {
TCHAR *bogus;
return str_number(string, number, &bogus);
}
/* Remove basename of a path. */
void strip_basename(TCHAR *buffer) {
size_t len = _tcslen(buffer);
size_t i;
for (i = len; i && buffer[i] != _T('\\') && buffer[i] != _T('/'); i--);
/* X:\ is OK. */
if (i && buffer[i - 1] == _T(':')) i++;
buffer[i] = _T('\0');
}
/* How to use me correctly */
int usage(int ret) {
if (GetConsoleWindow()) print_message(stderr, NSSM_MESSAGE_USAGE, NSSM_VERSION, NSSM_CONFIGURATION, NSSM_DATE);
else popup_message(0, MB_OK, NSSM_MESSAGE_USAGE, NSSM_VERSION, NSSM_CONFIGURATION, NSSM_DATE);
return(ret);
}
void check_admin() {
is_admin = false;
/* Lifted from MSDN examples */
PSID AdministratorsGroup;
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
if (! AllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &AdministratorsGroup)) return;
CheckTokenMembership(0, AdministratorsGroup, /*XXX*/(PBOOL) &is_admin);
FreeSid(AdministratorsGroup);
}
static int elevate(int argc, TCHAR **argv, unsigned long message) {
print_message(stderr, message);
SHELLEXECUTEINFO sei;
ZeroMemory(&sei, sizeof(sei));
sei.cbSize = sizeof(sei);
sei.lpVerb = _T("runas");
sei.lpFile = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, PATH_LENGTH);
if (! sei.lpFile) {
print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("GetModuleFileName()"), _T("elevate()"));
return 111;
}
GetModuleFileName(0, (TCHAR *) sei.lpFile, PATH_LENGTH);
TCHAR *args = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, EXE_LENGTH * sizeof(TCHAR));
if (! args) {
HeapFree(GetProcessHeap(), 0, (void *) sei.lpFile);
print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("GetCommandLine()"), _T("elevate()"));
return 111;
}
/* Get command line, which includes the path to NSSM, and skip that part. */
_sntprintf_s(args, EXE_LENGTH, _TRUNCATE, _T("%s"), GetCommandLine());
size_t s = _tcslen(argv[0]) + 1;
if (args[0] == _T('"')) s += 2;
while (isspace(args[s])) s++;
sei.lpParameters = args + s;
sei.nShow = SW_SHOW;
unsigned long exitcode = 0;
if (! ShellExecuteEx(&sei)) exitcode = 100;
HeapFree(GetProcessHeap(), 0, (void *) sei.lpFile);
HeapFree(GetProcessHeap(), 0, (void *) args);
return exitcode;
}
int num_cpus() {
DWORD_PTR i, affinity, system_affinity;
if (! GetProcessAffinityMask(GetCurrentProcess(), &affinity, &system_affinity)) return 64;
for (i = 0; system_affinity & (1LL << i); i++);
return (int) i;
}
int _tmain(int argc, TCHAR **argv) {
check_console();
#ifdef UNICODE
/*
Ensure we write in UTF-16 mode, so that non-ASCII characters don't get
mangled. If we were compiled in ANSI mode it won't work.
*/
_setmode(_fileno(stdout), _O_U16TEXT);
_setmode(_fileno(stderr), _O_U16TEXT);
#endif
/* Remember if we are admin */
check_admin();
/* Set up function pointers. */
if (get_imports()) exit(111);
/* Elevate */
if (argc > 1) {
/*
Valid commands are:
start, stop, pause, continue, install, edit, get, set, reset, unset, remove
*/
if (str_equiv(argv[1], _T("start"))) exit(control_service(NSSM_SERVICE_CONTROL_START, argc - 2, argv + 2));
if (str_equiv(argv[1], _T("stop"))) exit(control_service(SERVICE_CONTROL_STOP, argc - 2, argv + 2));
if (str_equiv(argv[1], _T("restart"))) {
int ret = control_service(SERVICE_CONTROL_STOP, argc - 2, argv + 2);
if (ret) exit(ret);
exit(control_service(NSSM_SERVICE_CONTROL_START, argc - 2, argv + 2));
}
if (str_equiv(argv[1], _T("pause"))) exit(control_service(SERVICE_CONTROL_PAUSE, argc - 2, argv + 2));
if (str_equiv(argv[1], _T("continue"))) exit(control_service(SERVICE_CONTROL_CONTINUE, argc - 2, argv + 2));
if (str_equiv(argv[1], _T("status"))) exit(control_service(SERVICE_CONTROL_INTERROGATE, argc - 2, argv + 2));
if (str_equiv(argv[1], _T("rotate"))) exit(control_service(NSSM_SERVICE_CONTROL_ROTATE, argc - 2, argv + 2));
if (str_equiv(argv[1], _T("install"))) {
if (! is_admin) exit(elevate(argc, argv, NSSM_MESSAGE_NOT_ADMINISTRATOR_CANNOT_INSTALL));
exit(pre_install_service(argc - 2, argv + 2));
}
if (str_equiv(argv[1], _T("edit")) || str_equiv(argv[1], _T("get")) || str_equiv(argv[1], _T("set")) || str_equiv(argv[1], _T("reset")) || str_equiv(argv[1], _T("unset"))) {
int ret = pre_edit_service(argc - 1, argv + 1);
if (ret == 3 && ! is_admin && argc == 3) exit(elevate(argc, argv, NSSM_MESSAGE_NOT_ADMINISTRATOR_CANNOT_EDIT));
/* There might be a password here. */
for (int i = 0; i < argc; i++) SecureZeroMemory(argv[i], _tcslen(argv[i]) * sizeof(TCHAR));
exit(ret);
}
if (str_equiv(argv[1], _T("remove"))) {
if (! is_admin) exit(elevate(argc, argv, NSSM_MESSAGE_NOT_ADMINISTRATOR_CANNOT_REMOVE));
exit(pre_remove_service(argc - 2, argv + 2));
}
}
/* Thread local storage for error message buffer */
tls_index = TlsAlloc();
/* Register messages */
if (is_admin) create_messages();
/*
Optimisation for Windows 2000:
When we're run from the command line the StartServiceCtrlDispatcher() call
will time out after a few seconds on Windows 2000. On newer versions the
call returns instantly. Check for stdin first and only try to call the
function if there's no input stream found. Although it's possible that
we're running with input redirected it's much more likely that we're
actually running as a service.
This will save time when running with no arguments from a command prompt.
*/
if (! GetStdHandle(STD_INPUT_HANDLE)) {
/* Start service magic */
SERVICE_TABLE_ENTRY table[] = { { NSSM, service_main }, { 0, 0 } };
if (! StartServiceCtrlDispatcher(table)) {
unsigned long error = GetLastError();
/* User probably ran nssm with no argument */
if (error == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) exit(usage(1));
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_DISPATCHER_FAILED, error_string(error), 0);
free_imports();
exit(100);
}
}
else exit(usage(1));
/* And nothing more to do */
exit(0);
}

@ -0,0 +1,136 @@
#ifndef NSSM_H
#define NSSM_H
/*
MSDN says, basically, that the maximum length of a path is 260 characters,
which is represented by the constant MAX_PATH. Except when it isn't.
The maximum length of a directory path is MAX_PATH - 12 because it must be
possible to create a file in 8.3 format under any valid directory.
Unicode versions of filesystem API functions accept paths up to 32767
characters if the first four (wide) characters are L"\\?\" and each component
of the path, separated by L"\", does not exceed the value of
lpMaximumComponentLength returned by GetVolumeInformation(), which is
probably 255. But might not be.
Relative paths are always limited to MAX_PATH because the L"\\?\" prefix
is not valid for a relative path.
Note that we don't care about the last two paragraphs because we're only
concerned with allocating buffers big enough to store valid paths. If the
user tries to store invalid paths they will fit in the buffers but the
application will fail. The reason for the failure will end up in the
event log and the user will realise the mistake.
So that's that cleared up, then.
*/
#ifdef UNICODE
#define PATH_LENGTH 32767
#else
#define PATH_LENGTH MAX_PATH
#endif
#define DIR_LENGTH PATH_LENGTH - 12
#define _WIN32_WINNT 0x0500
#include <fcntl.h>
#include <io.h>
#include <shlwapi.h>
#include <stdarg.h>
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#include "service.h"
#include "account.h"
#include "console.h"
#include "env.h"
#include "event.h"
#include "imports.h"
#include "messages.h"
#include "process.h"
#include "registry.h"
#include "settings.h"
#include "io.h"
#include "gui.h"
int str_equiv(const TCHAR *, const TCHAR *);
void strip_basename(TCHAR *);
int str_number(const TCHAR *, unsigned long *, TCHAR **);
int str_number(const TCHAR *, unsigned long *);
int num_cpus();
int usage(int);
#define NSSM _T("NSSM")
#ifdef _WIN64
#define NSSM_ARCHITECTURE _T("64-bit")
#else
#define NSSM_ARCHITECTURE _T("32-bit")
#endif
#ifdef _DEBUG
#define NSSM_DEBUG _T(" debug")
#else
#define NSSM_DEBUG _T("")
#endif
#define NSSM_CONFIGURATION NSSM_ARCHITECTURE NSSM_DEBUG
#include "version.h"
/*
Throttle the restart of the service if it stops before this many
milliseconds have elapsed since startup. Override in registry.
*/
#define NSSM_RESET_THROTTLE_RESTART 1500
/*
How many milliseconds to wait for the application to die after sending
a Control-C event to its console. Override in registry.
*/
#define NSSM_KILL_CONSOLE_GRACE_PERIOD 1500
/*
How many milliseconds to wait for the application to die after posting to
its windows' message queues. Override in registry.
*/
#define NSSM_KILL_WINDOW_GRACE_PERIOD 1500
/*
How many milliseconds to wait for the application to die after posting to
its threads' message queues. Override in registry.
*/
#define NSSM_KILL_THREADS_GRACE_PERIOD 1500
/* Margin of error for service status wait hints in milliseconds. */
#define NSSM_WAITHINT_MARGIN 2000
/* Methods used to try to stop the application. */
#define NSSM_STOP_METHOD_CONSOLE (1 << 0)
#define NSSM_STOP_METHOD_WINDOW (1 << 1)
#define NSSM_STOP_METHOD_THREADS (1 << 2)
#define NSSM_STOP_METHOD_TERMINATE (1 << 3)
/* Startup types. */
#define NSSM_STARTUP_AUTOMATIC 0
#define NSSM_STARTUP_DELAYED 1
#define NSSM_STARTUP_MANUAL 2
#define NSSM_STARTUP_DISABLED 3
/* Exit actions. */
#define NSSM_EXIT_RESTART 0
#define NSSM_EXIT_IGNORE 1
#define NSSM_EXIT_REALLY 2
#define NSSM_EXIT_UNCLEAN 3
#define NSSM_NUM_EXIT_ACTIONS 4
/* Process priority. */
#define NSSM_REALTIME_PRIORITY 0
#define NSSM_HIGH_PRIORITY 1
#define NSSM_ABOVE_NORMAL_PRIORITY 2
#define NSSM_NORMAL_PRIORITY 3
#define NSSM_BELOW_NORMAL_PRIORITY 4
#define NSSM_IDLE_PRIORITY 5
/* How many milliseconds to wait before updating service status. */
#define NSSM_SERVICE_STATUS_DEADLINE 20000
/* User-defined service controls can be in the range 128-255. */
#define NSSM_SERVICE_CONTROL_START 0
#define NSSM_SERVICE_CONTROL_ROTATE 128
#endif

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

@ -0,0 +1,26 @@

Microsoft Visual Studio Solution File, Format Version 10.00
# Visual Studio 2008
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nssm", "nssm.vcproj", "{32995E05-606F-4D83-A2E6-C2B361B34DF1}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Release|win64 = Release|win64
Release|win32 = Release|win32
Debug|win64 = Debug|win64
Debug|win32 = Debug|win32
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{32995E05-606F-4D83-A2E6-C2B361B34DF1}.Release|win64.ActiveCfg = Release|x64
{32995E05-606F-4D83-A2E6-C2B361B34DF1}.Release|win64.Build.0 = Release|x64
{32995E05-606F-4D83-A2E6-C2B361B34DF1}.Release|win32.ActiveCfg = Release|Win32
{32995E05-606F-4D83-A2E6-C2B361B34DF1}.Release|win32.Build.0 = Release|Win32
{32995E05-606F-4D83-A2E6-C2B361B34DF1}.Debug|win64.ActiveCfg = Debug|x64
{32995E05-606F-4D83-A2E6-C2B361B34DF1}.Debug|win64.Build.0 = Debug|x64
{32995E05-606F-4D83-A2E6-C2B361B34DF1}.Debug|win32.ActiveCfg = Debug|Win32
{32995E05-606F-4D83-A2E6-C2B361B34DF1}.Debug|win32.Build.0 = Debug|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

@ -0,0 +1,793 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="9.00"
Name="nssm"
ProjectGUID="{32995E05-606F-4D83-A2E6-C2B361B34DF1}"
RootNamespace="nssm"
TargetFrameworkVersion="0"
>
<Platforms>
<Platform
Name="Win32"
/>
<Platform
Name="x64"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="out\$(ConfigurationName)\win32"
IntermediateDirectory="tmp\$(ConfigurationName)\win32"
ConfigurationType="1"
InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
UseOfMFC="0"
ATLMinimizesCRunTimeLibraryUsage="false"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
Description="Setting version information"
CommandLine="version.cmd"
/>
<Tool
Name="VCCustomBuildTool"
Description=""
CommandLine=""
Outputs=""
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
TypeLibraryName="$(IntDir)/$(ProjectName).tlb"
HeaderFileName=""
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="1"
PrecompiledHeaderFile="$(IntDir)/$(ProjectName).pch"
AssemblerListingLocation="$(IntDir)/"
ObjectFile="$(IntDir)/"
ProgramDataBaseFileName="$(IntDir)/"
WarningLevel="3"
SuppressStartupBanner="true"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
PreprocessorDefinitions="_DEBUG"
Culture="2057"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="shlwapi.lib"
LinkIncremental="2"
SuppressStartupBanner="true"
GenerateDebugInformation="true"
ProgramDatabaseFile="$(OutDir)/$(ProjectName).pdb"
SubSystem="1"
RandomizedBaseAddress="1"
DataExecutionPrevention="0"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
SuppressStartupBanner="true"
OutputFile="$(IntDir)/$(ProjectName).bsc"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Debug|x64"
OutputDirectory="out\$(ConfigurationName)\win64"
IntermediateDirectory="tmp\$(ConfigurationName)\win64"
ConfigurationType="1"
InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
UseOfMFC="0"
ATLMinimizesCRunTimeLibraryUsage="false"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
Description="Setting version information"
CommandLine="version.cmd"
/>
<Tool
Name="VCCustomBuildTool"
Description=""
CommandLine=""
Outputs=""
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
TargetEnvironment="3"
TypeLibraryName="$(IntDir)/$(ProjectName).tlb"
HeaderFileName=""
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="1"
PrecompiledHeaderFile="$(IntDir)/$(ProjectName).pch"
AssemblerListingLocation="$(IntDir)/"
ObjectFile="$(IntDir)/"
ProgramDataBaseFileName="$(IntDir)/"
WarningLevel="3"
SuppressStartupBanner="true"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
PreprocessorDefinitions="_DEBUG;_WIN64"
Culture="2057"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="shlwapi.lib"
LinkIncremental="2"
SuppressStartupBanner="true"
GenerateDebugInformation="true"
ProgramDatabaseFile="$(OutDir)/$(ProjectName).pdb"
SubSystem="1"
RandomizedBaseAddress="1"
DataExecutionPrevention="0"
TargetMachine="17"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
SuppressStartupBanner="true"
OutputFile="$(IntDir)/$(ProjectName).bsc"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="out\$(ConfigurationName)\win32"
IntermediateDirectory="tmp\$(ConfigurationName)\win32"
ConfigurationType="1"
InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
UseOfMFC="0"
ATLMinimizesCRunTimeLibraryUsage="false"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
Description="Setting version information"
CommandLine="version.cmd"
/>
<Tool
Name="VCCustomBuildTool"
Description=""
CommandLine=""
Outputs=""
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
TypeLibraryName="$(IntDir)/$(ProjectName).tlb"
HeaderFileName=""
/>
<Tool
Name="VCCLCompilerTool"
Optimization="2"
InlineFunctionExpansion="1"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
StringPooling="true"
RuntimeLibrary="0"
EnableFunctionLevelLinking="true"
PrecompiledHeaderFile="$(IntDir)/$(ProjectName).pch"
AssemblerListingLocation="$(IntDir)/"
ObjectFile="$(IntDir)/"
ProgramDataBaseFileName="$(IntDir)/"
WarningLevel="3"
SuppressStartupBanner="true"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
PreprocessorDefinitions="NDEBUG"
Culture="2057"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="shlwapi.lib"
LinkIncremental="1"
SuppressStartupBanner="true"
ProgramDatabaseFile="$(OutDir)/$(ProjectName).pdb"
SubSystem="1"
RandomizedBaseAddress="1"
DataExecutionPrevention="0"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
SuppressStartupBanner="true"
OutputFile="$(IntDir)/$(ProjectName).bsc"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|x64"
OutputDirectory="out\$(ConfigurationName)\win64"
IntermediateDirectory="tmp\$(ConfigurationName)\win64"
ConfigurationType="1"
InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC60.vsprops"
UseOfMFC="0"
ATLMinimizesCRunTimeLibraryUsage="false"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
Description="Setting version information"
CommandLine="version.cmd"
/>
<Tool
Name="VCCustomBuildTool"
Description=""
CommandLine=""
Outputs=""
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
TargetEnvironment="3"
TypeLibraryName="$(IntDir)/$(ProjectName).tlb"
HeaderFileName=""
/>
<Tool
Name="VCCLCompilerTool"
Optimization="2"
InlineFunctionExpansion="1"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
StringPooling="true"
RuntimeLibrary="0"
EnableFunctionLevelLinking="true"
PrecompiledHeaderFile="$(IntDir)/$(ProjectName).pch"
AssemblerListingLocation="$(IntDir)/"
ObjectFile="$(IntDir)/"
ProgramDataBaseFileName="$(IntDir)/"
WarningLevel="3"
SuppressStartupBanner="true"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
PreprocessorDefinitions="NDEBUG;_WIN64"
Culture="2057"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="shlwapi.lib"
LinkIncremental="1"
SuppressStartupBanner="true"
ProgramDatabaseFile="$(OutDir)/$(ProjectName).pdb"
SubSystem="1"
RandomizedBaseAddress="1"
DataExecutionPrevention="0"
TargetMachine="17"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
SuppressStartupBanner="true"
OutputFile="$(IntDir)/$(ProjectName).bsc"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Source Files"
Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
>
<File
RelativePath="account.cpp"
>
</File>
<File
RelativePath="console.cpp"
>
</File>
<File
RelativePath="env.cpp"
>
</File>
<File
RelativePath="event.cpp"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions=""
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|x64"
>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions=""
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions=""
/>
</FileConfiguration>
<FileConfiguration
Name="Release|x64"
>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions=""
/>
</FileConfiguration>
</File>
<File
RelativePath="gui.cpp"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions=""
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|x64"
>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions=""
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions=""
/>
</FileConfiguration>
<FileConfiguration
Name="Release|x64"
>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions=""
/>
</FileConfiguration>
</File>
<File
RelativePath="imports.cpp"
>
</File>
<File
RelativePath="io.cpp"
>
</File>
<File
RelativePath="nssm.cpp"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions=""
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|x64"
>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions=""
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions=""
/>
</FileConfiguration>
<FileConfiguration
Name="Release|x64"
>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions=""
/>
</FileConfiguration>
</File>
<File
RelativePath="process.cpp"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions=""
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|x64"
>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions=""
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions=""
/>
</FileConfiguration>
<FileConfiguration
Name="Release|x64"
>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions=""
/>
</FileConfiguration>
</File>
<File
RelativePath="registry.cpp"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions=""
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|x64"
>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions=""
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions=""
/>
</FileConfiguration>
<FileConfiguration
Name="Release|x64"
>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions=""
/>
</FileConfiguration>
</File>
<File
RelativePath="service.cpp"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions=""
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|x64"
>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions=""
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions=""
/>
</FileConfiguration>
<FileConfiguration
Name="Release|x64"
>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions=""
/>
</FileConfiguration>
</File>
<File
RelativePath="settings.cpp"
>
</File>
</Filter>
<Filter
Name="Header Files"
Filter="h;hpp;hxx;hm;inl"
>
<File
RelativePath="account.h"
>
</File>
<File
RelativePath="console.h"
>
</File>
<File
RelativePath="env.h"
>
</File>
<File
RelativePath="event.h"
>
</File>
<File
RelativePath="gui.h"
>
</File>
<File
RelativePath="imports.h"
>
</File>
<File
RelativePath="io.h"
>
</File>
<File
RelativePath="nssm.h"
>
</File>
<File
RelativePath="process.h"
>
</File>
<File
RelativePath="registry.h"
>
</File>
<File
RelativePath="service.h"
>
</File>
<File
RelativePath="settings.h"
>
</File>
</Filter>
<Filter
Name="Resource Files"
Filter="ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
>
<File
RelativePath="nssm.ico"
>
</File>
<File
RelativePath="nssm.rc"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCResourceCompilerTool"
PreprocessorDefinitions=""
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|x64"
>
<Tool
Name="VCResourceCompilerTool"
PreprocessorDefinitions=""
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCResourceCompilerTool"
PreprocessorDefinitions=""
/>
</FileConfiguration>
<FileConfiguration
Name="Release|x64"
>
<Tool
Name="VCResourceCompilerTool"
PreprocessorDefinitions=""
/>
</FileConfiguration>
</File>
</Filter>
<File
RelativePath="messages.mc"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCustomBuildTool"
Description="Compiling messages"
CommandLine="mc -u -U $(InputName).mc -r . -h ."
Outputs="$(InputName).rc;$(InputName).h"
/>
</FileConfiguration>
<FileConfiguration
Name="Debug|x64"
>
<Tool
Name="VCCustomBuildTool"
Description="Compiling messages"
CommandLine="mc -u -U $(InputName).mc -r . -h ."
Outputs="$(InputName).rc;$(InputName).h"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCustomBuildTool"
Description="Compiling messages"
CommandLine="mc -u -U $(InputName).mc -r . -h ."
AdditionalDependencies=""
Outputs="$(InputName).rc;$(InputName).h"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|x64"
>
<Tool
Name="VCCustomBuildTool"
Description="Compiling messages"
CommandLine="mc -u -U $(InputName).mc -r . -h ."
AdditionalDependencies=""
Outputs="$(InputName).rc;$(InputName).h"
/>
</FileConfiguration>
</File>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

@ -0,0 +1,308 @@
#include "nssm.h"
extern imports_t imports;
int get_process_creation_time(HANDLE process_handle, FILETIME *ft) {
FILETIME creation_time, exit_time, kernel_time, user_time;
if (! GetProcessTimes(process_handle, &creation_time, &exit_time, &kernel_time, &user_time)) {
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GETPROCESSTIMES_FAILED, error_string(GetLastError()), 0);
return 1;
}
memmove(ft, &creation_time, sizeof(creation_time));
return 0;
}
int get_process_exit_time(HANDLE process_handle, FILETIME *ft) {
FILETIME creation_time, exit_time, kernel_time, user_time;
if (! GetProcessTimes(process_handle, &creation_time, &exit_time, &kernel_time, &user_time)) {
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GETPROCESSTIMES_FAILED, error_string(GetLastError()), 0);
return 1;
}
if (! (exit_time.dwLowDateTime || exit_time.dwHighDateTime)) return 2;
memmove(ft, &exit_time, sizeof(exit_time));
return 0;
}
int check_parent(nssm_service_t *service, PROCESSENTRY32 *pe, unsigned long ppid) {
/* Check parent process ID matches. */
if (pe->th32ParentProcessID != ppid) return 1;
/*
Process IDs can be reused so do a sanity check by making sure the child
has been running for less time than the parent.
Though unlikely, it's possible that the parent exited and its process ID
was already reused, so we'll also compare against its exit time.
*/
HANDLE process_handle = OpenProcess(PROCESS_QUERY_INFORMATION, false, pe->th32ProcessID);
if (! process_handle) {
TCHAR pid_string[16];
_sntprintf_s(pid_string, _countof(pid_string), _TRUNCATE, _T("%lu"), pe->th32ProcessID);
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENPROCESS_FAILED, pid_string, service->name, error_string(GetLastError()), 0);
return 2;
}
FILETIME ft;
if (get_process_creation_time(process_handle, &ft)) {
CloseHandle(process_handle);
return 3;
}
CloseHandle(process_handle);
/* Verify that the parent's creation time is not later. */
if (CompareFileTime(&service->creation_time, &ft) > 0) return 4;
/* Verify that the parent's exit time is not earlier. */
if (CompareFileTime(&service->exit_time, &ft) < 0) return 5;
return 0;
}
/* Send some window messages and hope the window respects one or more. */
int CALLBACK kill_window(HWND window, LPARAM arg) {
kill_t *k = (kill_t *) arg;
unsigned long pid;
if (! GetWindowThreadProcessId(window, &pid)) return 1;
if (pid != k->pid) return 1;
/* First try sending WM_CLOSE to request that the window close. */
k->signalled |= PostMessage(window, WM_CLOSE, k->exitcode, 0);
/*
Then tell the window that the user is logging off and it should exit
without worrying about saving any data.
*/
k->signalled |= PostMessage(window, WM_ENDSESSION, 1, ENDSESSION_CLOSEAPP | ENDSESSION_CRITICAL | ENDSESSION_LOGOFF);
return 1;
}
/*
Try to post a message to the message queues of threads associated with the
given process ID. Not all threads have message queues so there's no
guarantee of success, and we don't want to be left waiting for unsignalled
processes so this function returns only true if at least one thread was
successfully prodded.
*/
int kill_threads(TCHAR *service_name, kill_t *k) {
int ret = 0;
/* Get a snapshot of all threads in the system. */
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
if (! snapshot) {
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETOOLHELP32SNAPSHOT_THREAD_FAILED, service_name, error_string(GetLastError()), 0);
return 0;
}
THREADENTRY32 te;
ZeroMemory(&te, sizeof(te));
te.dwSize = sizeof(te);
if (! Thread32First(snapshot, &te)) {
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_THREAD_ENUMERATE_FAILED, service_name, error_string(GetLastError()), 0);
CloseHandle(snapshot);
return 0;
}
/* This thread belongs to the doomed process so signal it. */
if (te.th32OwnerProcessID == k->pid) {
ret |= PostThreadMessage(te.th32ThreadID, WM_QUIT, k->exitcode, 0);
}
while (true) {
/* Try to get the next thread. */
if (! Thread32Next(snapshot, &te)) {
unsigned long error = GetLastError();
if (error == ERROR_NO_MORE_FILES) break;
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_THREAD_ENUMERATE_FAILED, service_name, error_string(GetLastError()), 0);
CloseHandle(snapshot);
return ret;
}
if (te.th32OwnerProcessID == k->pid) {
ret |= PostThreadMessage(te.th32ThreadID, WM_QUIT, k->exitcode, 0);
}
}
CloseHandle(snapshot);
return ret;
}
/* Give the process a chance to die gracefully. */
int kill_process(nssm_service_t *service, HANDLE process_handle, unsigned long pid, unsigned long exitcode) {
/* Shouldn't happen. */
if (! service) return 1;
if (! pid) return 1;
if (! process_handle) return 1;
unsigned long ret;
if (GetExitCodeProcess(process_handle, &ret)) {
if (ret != STILL_ACTIVE) return 1;
}
kill_t k = { pid, exitcode, 0 };
/* Try to send a Control-C event to the console. */
if (service->stop_method & NSSM_STOP_METHOD_CONSOLE) {
if (! kill_console(service, &k)) return 1;
}
/*
Try to post messages to the windows belonging to the given process ID.
If the process is a console application it won't have any windows so there's
no guarantee of success.
*/
if (service->stop_method & NSSM_STOP_METHOD_WINDOW) {
EnumWindows((WNDENUMPROC) kill_window, (LPARAM) &k);
if (k.signalled) {
if (! await_shutdown(service, _T(__FUNCTION__), service->kill_window_delay)) return 1;
}
}
/*
Try to post messages to any thread message queues associated with the
process. Console applications might have them (but probably won't) so
there's still no guarantee of success.
*/
if (service->stop_method & NSSM_STOP_METHOD_THREADS) {
if (kill_threads(service->name, &k)) {
if (! await_shutdown(service, _T(__FUNCTION__), service->kill_threads_delay)) return 1;
}
}
/* We tried being nice. Time for extreme prejudice. */
if (service->stop_method & NSSM_STOP_METHOD_TERMINATE) {
return TerminateProcess(process_handle, exitcode);
}
return 0;
}
/* Simulate a Control-C event to our console (shared with the app). */
int kill_console(nssm_service_t *service, kill_t *k) {
unsigned long ret;
if (! service) return 1;
/* Check we loaded AttachConsole(). */
if (! imports.AttachConsole) return 4;
/* Try to attach to the process's console. */
if (! imports.AttachConsole(k->pid)) {
ret = GetLastError();
switch (ret) {
case ERROR_INVALID_HANDLE:
/* The app doesn't have a console. */
return 1;
case ERROR_GEN_FAILURE:
/* The app already exited. */
return 2;
case ERROR_ACCESS_DENIED:
default:
/* We already have a console. */
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_ATTACHCONSOLE_FAILED, service->name, error_string(ret), 0);
return 3;
}
}
/* Ignore the event ourselves. */
ret = 0;
if (! SetConsoleCtrlHandler(0, TRUE)) {
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETCONSOLECTRLHANDLER_FAILED, service->name, error_string(GetLastError()), 0);
ret = 4;
}
/* Send the event. */
if (! ret) {
if (! GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0)) {
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GENERATECONSOLECTRLEVENT_FAILED, service->name, error_string(GetLastError()), 0);
ret = 5;
}
}
/* Detach from the console. */
if (! FreeConsole()) {
log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_FREECONSOLE_FAILED, service->name, error_string(GetLastError()), 0);
}
/* Wait for process to exit. */
if (await_shutdown(service, _T(__FUNCTION__), service->kill_console_delay)) ret = 6;
return ret;
}
void kill_process_tree(nssm_service_t *service, unsigned long pid, unsigned long exitcode, unsigned long ppid) {
/* Shouldn't happen unless the service failed to start. */
if (! pid) return;
TCHAR pid_string[16], code[16];
_sntprintf_s(pid_string, _countof(pid_string), _TRUNCATE, _T("%lu"), pid);
_sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), exitcode);
log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_KILLING, service->name, pid_string, code, 0);
/* We will need a process handle in order to call TerminateProcess() later. */
HANDLE process_handle = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_TERMINATE, false, pid);
if (process_handle) {
/* Kill this process first, then its descendents. */
TCHAR ppid_string[16];
_sntprintf_s(ppid_string, _countof(ppid_string), _TRUNCATE, _T("%lu"), ppid);
log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_KILL_PROCESS_TREE, pid_string, ppid_string, service->name, 0);
if (! kill_process(service, process_handle, pid, exitcode)) {
/* Maybe it already died. */
unsigned long ret;
if (! GetExitCodeProcess(process_handle, &ret) || ret == STILL_ACTIVE) {
if (service->stop_method & NSSM_STOP_METHOD_TERMINATE) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_TERMINATEPROCESS_FAILED, pid_string, service->name, error_string(GetLastError()), 0);
else log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_PROCESS_STILL_ACTIVE, service->name, pid_string, NSSM, NSSM_REG_STOP_METHOD_SKIP, 0);
}
}
CloseHandle(process_handle);
}
else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENPROCESS_FAILED, pid_string, service->name, error_string(GetLastError()), 0);
/* Get a snapshot of all processes in the system. */
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (! snapshot) {
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETOOLHELP32SNAPSHOT_PROCESS_FAILED, service->name, error_string(GetLastError()), 0);
return;
}
PROCESSENTRY32 pe;
ZeroMemory(&pe, sizeof(pe));
pe.dwSize = sizeof(pe);
if (! Process32First(snapshot, &pe)) {
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_PROCESS_ENUMERATE_FAILED, service->name, error_string(GetLastError()), 0);
CloseHandle(snapshot);
return;
}
/* This is a child of the doomed process so kill it. */
if (! check_parent(service, &pe, pid)) kill_process_tree(service, pe.th32ProcessID, exitcode, ppid);
while (true) {
/* Try to get the next process. */
if (! Process32Next(snapshot, &pe)) {
unsigned long ret = GetLastError();
if (ret == ERROR_NO_MORE_FILES) break;
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_PROCESS_ENUMERATE_FAILED, service->name, error_string(GetLastError()), 0);
CloseHandle(snapshot);
return;
}
if (! check_parent(service, &pe, pid)) kill_process_tree(service, pe.th32ProcessID, exitcode, ppid);
}
CloseHandle(snapshot);
}

@ -0,0 +1,21 @@
#ifndef PROCESS_H
#define PROCESS_H
#include <tlhelp32.h>
typedef struct {
unsigned long pid;
unsigned long exitcode;
int signalled;
} kill_t;
int get_process_creation_time(HANDLE, FILETIME *);
int get_process_exit_time(HANDLE, FILETIME *);
int check_parent(TCHAR *, PROCESSENTRY32 *, unsigned long, FILETIME *, FILETIME *);
int CALLBACK kill_window(HWND, LPARAM);
int kill_threads(nssm_service_t *, kill_t *);
int kill_console(nssm_service_t *, kill_t *);
int kill_process(nssm_service_t *, HANDLE, unsigned long, unsigned long);
void kill_process_tree(nssm_service_t *, unsigned long, unsigned long, unsigned long);
#endif

@ -0,0 +1,714 @@
#include "nssm.h"
extern const TCHAR *exit_action_strings[];
int create_messages() {
HKEY key;
TCHAR registry[KEY_LENGTH];
if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, _T("SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\%s"), NSSM) < 0) {
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("eventlog registry"), _T("create_messages()"), 0);
return 1;
}
if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry, 0, 0, REG_OPTION_NON_VOLATILE, KEY_WRITE, 0, &key, 0) != ERROR_SUCCESS) {
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
return 2;
}
/* Get path of this program */
TCHAR path[PATH_LENGTH];
GetModuleFileName(0, path, _countof(path));
/* Try to register the module but don't worry so much on failure */
RegSetValueEx(key, _T("EventMessageFile"), 0, REG_SZ, (const unsigned char *) path, (unsigned long) (_tcslen(path) + 1) * sizeof(TCHAR));
unsigned long types = EVENTLOG_INFORMATION_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_ERROR_TYPE;
RegSetValueEx(key, _T("TypesSupported"), 0, REG_DWORD, (const unsigned char *) &types, sizeof(types));
return 0;
}
int create_parameters(nssm_service_t *service, bool editing) {
/* Try to open the registry */
HKEY key = open_registry(service->name, KEY_WRITE);
if (! key) return 1;
/* Try to create the parameters */
if (set_expand_string(key, NSSM_REG_EXE, service->exe)) {
RegDeleteKey(HKEY_LOCAL_MACHINE, NSSM_REGISTRY);
RegCloseKey(key);
return 2;
}
if (set_expand_string(key, NSSM_REG_FLAGS, service->flags)) {
RegDeleteKey(HKEY_LOCAL_MACHINE, NSSM_REGISTRY);
RegCloseKey(key);
return 3;
}
if (set_expand_string(key, NSSM_REG_DIR, service->dir)) {
RegDeleteKey(HKEY_LOCAL_MACHINE, NSSM_REGISTRY);
RegCloseKey(key);
return 4;
}
/* Other non-default parameters. May fail. */
if (service->priority != NORMAL_PRIORITY_CLASS) set_number(key, NSSM_REG_PRIORITY, service->priority);
else if (editing) RegDeleteValue(key, NSSM_REG_PRIORITY);
if (service->affinity) {
TCHAR *string;
if (! affinity_mask_to_string(service->affinity, &string)) {
if (RegSetValueEx(key, NSSM_REG_AFFINITY, 0, REG_SZ, (const unsigned char *) string, (unsigned long) (_tcslen(string) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) {
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_AFFINITY, error_string(GetLastError()), 0);
HeapFree(GetProcessHeap(), 0, string);
return 5;
}
}
if (string) HeapFree(GetProcessHeap(), 0, string);
}
else if (editing) RegDeleteValue(key, NSSM_REG_AFFINITY);
unsigned long stop_method_skip = ~service->stop_method;
if (stop_method_skip) set_number(key, NSSM_REG_STOP_METHOD_SKIP, stop_method_skip);
else if (editing) RegDeleteValue(key, NSSM_REG_STOP_METHOD_SKIP);
if (service->default_exit_action < NSSM_NUM_EXIT_ACTIONS) create_exit_action(service->name, exit_action_strings[service->default_exit_action], editing);
if (service->restart_delay) set_number(key, NSSM_REG_RESTART_DELAY, service->restart_delay);
else if (editing) RegDeleteValue(key, NSSM_REG_RESTART_DELAY);
if (service->throttle_delay != NSSM_RESET_THROTTLE_RESTART) set_number(key, NSSM_REG_THROTTLE, service->throttle_delay);
else if (editing) RegDeleteValue(key, NSSM_REG_THROTTLE);
if (service->kill_console_delay != NSSM_KILL_CONSOLE_GRACE_PERIOD) set_number(key, NSSM_REG_KILL_CONSOLE_GRACE_PERIOD, service->kill_console_delay);
else if (editing) RegDeleteValue(key, NSSM_REG_KILL_CONSOLE_GRACE_PERIOD);
if (service->kill_window_delay != NSSM_KILL_WINDOW_GRACE_PERIOD) set_number(key, NSSM_REG_KILL_WINDOW_GRACE_PERIOD, service->kill_window_delay);
else if (editing) RegDeleteValue(key, NSSM_REG_KILL_WINDOW_GRACE_PERIOD);
if (service->kill_threads_delay != NSSM_KILL_THREADS_GRACE_PERIOD) set_number(key, NSSM_REG_KILL_THREADS_GRACE_PERIOD, service->kill_threads_delay);
else if (editing) RegDeleteValue(key, NSSM_REG_KILL_THREADS_GRACE_PERIOD);
if (service->stdin_path[0] || editing) {
if (service->stdin_path[0]) set_expand_string(key, NSSM_REG_STDIN, service->stdin_path);
else if (editing) RegDeleteValue(key, NSSM_REG_STDIN);
if (service->stdin_sharing != NSSM_STDIN_SHARING) set_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_SHARING, service->stdin_sharing);
else if (editing) delete_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_SHARING);
if (service->stdin_disposition != NSSM_STDIN_DISPOSITION) set_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_DISPOSITION, service->stdin_disposition);
else if (editing) delete_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_DISPOSITION);
if (service->stdin_flags != NSSM_STDIN_FLAGS) set_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_FLAGS, service->stdin_flags);
else if (editing) delete_createfile_parameter(key, NSSM_REG_STDIN, NSSM_REG_STDIO_FLAGS);
}
if (service->stdout_path[0] || editing) {
if (service->stdout_path[0]) set_expand_string(key, NSSM_REG_STDOUT, service->stdout_path);
else if (editing) RegDeleteValue(key, NSSM_REG_STDOUT);
if (service->stdout_sharing != NSSM_STDOUT_SHARING) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_SHARING, service->stdout_sharing);
else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_SHARING);
if (service->stdout_disposition != NSSM_STDOUT_DISPOSITION) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_DISPOSITION, service->stdout_disposition);
else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_DISPOSITION);
if (service->stdout_flags != NSSM_STDOUT_FLAGS) set_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_FLAGS, service->stdout_flags);
else if (editing) delete_createfile_parameter(key, NSSM_REG_STDOUT, NSSM_REG_STDIO_FLAGS);
}
if (service->stderr_path[0] || editing) {
if (service->stderr_path[0]) set_expand_string(key, NSSM_REG_STDERR, service->stderr_path);
else if (editing) RegDeleteValue(key, NSSM_REG_STDERR);
if (service->stderr_sharing != NSSM_STDERR_SHARING) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_SHARING, service->stderr_sharing);
else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_SHARING);
if (service->stderr_disposition != NSSM_STDERR_DISPOSITION) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_DISPOSITION, service->stderr_disposition);
else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_DISPOSITION);
if (service->stderr_flags != NSSM_STDERR_FLAGS) set_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_FLAGS, service->stderr_flags);
else if (editing) delete_createfile_parameter(key, NSSM_REG_STDERR, NSSM_REG_STDIO_FLAGS);
}
if (service->rotate_files) set_number(key, NSSM_REG_ROTATE, 1);
else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE);
if (service->rotate_stdout_online) set_number(key, NSSM_REG_ROTATE_ONLINE, 1);
else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_ONLINE);
if (service->rotate_seconds) set_number(key, NSSM_REG_ROTATE_SECONDS, service->rotate_seconds);
else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_SECONDS);
if (service->rotate_bytes_low) set_number(key, NSSM_REG_ROTATE_BYTES_LOW, service->rotate_bytes_low);
else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_BYTES_LOW);
if (service->rotate_bytes_high) set_number(key, NSSM_REG_ROTATE_BYTES_HIGH, service->rotate_bytes_high);
else if (editing) RegDeleteValue(key, NSSM_REG_ROTATE_BYTES_HIGH);
if (service->no_console) set_number(key, NSSM_REG_NO_CONSOLE, 1);
else if (editing) RegDeleteValue(key, NSSM_REG_NO_CONSOLE);
/* Environment */
if (service->env) {
if (RegSetValueEx(key, NSSM_REG_ENV, 0, REG_MULTI_SZ, (const unsigned char *) service->env, (unsigned long) service->envlen * sizeof(TCHAR)) != ERROR_SUCCESS) {
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV, error_string(GetLastError()), 0);
}
}
else if (editing) RegDeleteValue(key, NSSM_REG_ENV);
if (service->env_extra) {
if (RegSetValueEx(key, NSSM_REG_ENV_EXTRA, 0, REG_MULTI_SZ, (const unsigned char *) service->env_extra, (unsigned long) service->env_extralen * sizeof(TCHAR)) != ERROR_SUCCESS) {
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV_EXTRA, error_string(GetLastError()), 0);
}
}
else if (editing) RegDeleteValue(key, NSSM_REG_ENV_EXTRA);
/* Close registry. */
RegCloseKey(key);
return 0;
}
int create_exit_action(TCHAR *service_name, const TCHAR *action_string, bool editing) {
/* Get registry */
TCHAR registry[KEY_LENGTH];
if (_sntprintf_s(registry, _countof(registry), _TRUNCATE, NSSM_REGISTRY _T("\\%s"), service_name, NSSM_REG_EXIT) < 0) {
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("NSSM_REG_EXIT"), _T("create_exit_action()"), 0);
return 1;
}
/* Try to open the registry */
HKEY key;
unsigned long disposition;
if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry, 0, 0, REG_OPTION_NON_VOLATILE, KEY_WRITE, 0, &key, &disposition) != ERROR_SUCCESS) {
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
return 2;
}
/* Do nothing if the key already existed */
if (disposition == REG_OPENED_EXISTING_KEY && ! editing) {
RegCloseKey(key);
return 0;
}
/* Create the default value */
if (RegSetValueEx(key, 0, 0, REG_SZ, (const unsigned char *) action_string, (unsigned long) (_tcslen(action_string) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) {
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_EXIT, error_string(GetLastError()), 0);
RegCloseKey(key);
return 3;
}
/* Close registry */
RegCloseKey(key);
return 0;
}
int get_environment(TCHAR *service_name, HKEY key, TCHAR *value, TCHAR **env, unsigned long *envlen) {
unsigned long type = REG_MULTI_SZ;
/* Dummy test to find buffer size */
unsigned long ret = RegQueryValueEx(key, value, 0, &type, NULL, envlen);
if (ret != ERROR_SUCCESS) {
*env = 0;
*envlen = 0;
/* The service probably doesn't have any environment configured */
if (ret == ERROR_FILE_NOT_FOUND) return 0;
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
return 1;
}
if (type != REG_MULTI_SZ) {
*env = 0;
*envlen = 0;
log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_INVALID_ENVIRONMENT_STRING_TYPE, value, service_name, 0);
return 2;
}
/* Probably not possible */
if (! *envlen) return 0;
/* Previously initialised? */
if (*env) HeapFree(GetProcessHeap(), 0, *env);
*env = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, *envlen);
if (! *env) {
*envlen = 0;
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, value, _T("get_environment()"), 0);
return 3;
}
/* Actually get the strings */
ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) *env, envlen);
if (ret != ERROR_SUCCESS) {
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
HeapFree(GetProcessHeap(), 0, *env);
*env = 0;
*envlen = 0;
return 4;
}
return 0;
}
int get_string(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool expand, bool sanitise, bool must_exist) {
TCHAR *buffer = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, datalen);
if (! buffer) {
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, value, _T("get_string()"), 0);
return 1;
}
ZeroMemory(data, datalen);
unsigned long type = REG_EXPAND_SZ;
unsigned long buflen = datalen;
unsigned long ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) buffer, &buflen);
if (ret != ERROR_SUCCESS) {
unsigned long error = GetLastError();
HeapFree(GetProcessHeap(), 0, buffer);
if (ret == ERROR_FILE_NOT_FOUND) {
if (! must_exist) return 0;
}
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(error), 0);
return 2;
}
/* Paths aren't allowed to contain quotes. */
if (sanitise) PathUnquoteSpaces(buffer);
/* Do we want to expand the string? */
if (! expand) {
if (type == REG_EXPAND_SZ) type = REG_SZ;
}
/* Technically we shouldn't expand environment strings from REG_SZ values */
if (type != REG_EXPAND_SZ) {
memmove(data, buffer, buflen);
HeapFree(GetProcessHeap(), 0, buffer);
return 0;
}
ret = ExpandEnvironmentStrings((TCHAR *) buffer, data, datalen);
if (! ret || ret > datalen) {
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_EXPANDENVIRONMENTSTRINGS_FAILED, buffer, error_string(GetLastError()), 0);
HeapFree(GetProcessHeap(), 0, buffer);
return 3;
}
HeapFree(GetProcessHeap(), 0, buffer);
return 0;
}
int get_string(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise) {
return get_string(key, value, data, datalen, false, sanitise, true);
}
int expand_parameter(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise, bool must_exist) {
return get_string(key, value, data, datalen, true, sanitise, must_exist);
}
int expand_parameter(HKEY key, TCHAR *value, TCHAR *data, unsigned long datalen, bool sanitise) {
return expand_parameter(key, value, data, datalen, sanitise, true);
}
/*
Sets a string in the registry.
Returns: 0 if it was set.
1 on error.
*/
int set_string(HKEY key, TCHAR *value, TCHAR *string, bool expand) {
unsigned long type = expand ? REG_EXPAND_SZ : REG_SZ;
if (RegSetValueEx(key, value, 0, type, (const unsigned char *) string, (unsigned long) (_tcslen(string) + 1) * sizeof(TCHAR)) == ERROR_SUCCESS) return 0;
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, value, error_string(GetLastError()), 0);
return 1;
}
int set_string(HKEY key, TCHAR *value, TCHAR *string) {
return set_string(key, value, string, false);
return 1;
}
int set_expand_string(HKEY key, TCHAR *value, TCHAR *string) {
return set_string(key, value, string, true);
return 1;
}
/*
Set an unsigned long in the registry.
Returns: 0 if it was set.
1 on error.
*/
int set_number(HKEY key, TCHAR *value, unsigned long number) {
if (RegSetValueEx(key, value, 0, REG_DWORD, (const unsigned char *) &number, sizeof(number)) == ERROR_SUCCESS) return 0;
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, value, error_string(GetLastError()), 0);
return 1;
}
/*
Query an unsigned long from the registry.
Returns: 1 if a number was retrieved.
0 if none was found and must_exist is false.
-1 if none was found and must_exist is true.
-2 otherwise.
*/
int get_number(HKEY key, TCHAR *value, unsigned long *number, bool must_exist) {
unsigned long type = REG_DWORD;
unsigned long number_len = sizeof(unsigned long);
int ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) number, &number_len);
if (ret == ERROR_SUCCESS) return 1;
if (ret == ERROR_FILE_NOT_FOUND) {
if (! must_exist) return 0;
}
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
if (ret == ERROR_FILE_NOT_FOUND) return -1;
return -2;
}
int get_number(HKEY key, TCHAR *value, unsigned long *number) {
return get_number(key, value, number, true);
}
/* Replace NULL with CRLF. Leave NULL NULL as the end marker. */
int format_double_null(TCHAR *dn, unsigned long dnlen, TCHAR **formatted, unsigned long *newlen) {
unsigned long i, j;
*newlen = dnlen;
if (! *newlen) {
*formatted = 0;
return 0;
}
for (i = 0; i < dnlen; i++) if (! dn[i] && dn[i + 1]) ++*newlen;
*formatted = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *newlen * sizeof(TCHAR));
if (! *formatted) {
*newlen = 0;
return 1;
}
for (i = 0, j = 0; i < dnlen; i++) {
(*formatted)[j] = dn[i];
if (! dn[i]) {
if (dn[i + 1]) {
(*formatted)[j] = _T('\r');
(*formatted)[++j] = _T('\n');
}
}
j++;
}
return 0;
}
/* Strip CR and replace LF with NULL. */
int unformat_double_null(TCHAR *dn, unsigned long dnlen, TCHAR **unformatted, unsigned long *newlen) {
unsigned long i, j;
*newlen = 0;
if (! dnlen) {
*unformatted = 0;
return 0;
}
for (i = 0; i < dnlen; i++) if (dn[i] != _T('\r')) ++*newlen;
/* Skip blank lines. */
for (i = 0; i < dnlen; i++) {
if (dn[i] == _T('\r') && dn[i + 1] == _T('\n')) {
/* This is the last CRLF. */
if (i >= dnlen - 2) break;
/*
Strip at the start of the block or if the next characters are
CRLF too.
*/
if (! i || (dn[i + 2] == _T('\r') && dn[i + 3] == _T('\n'))) {
for (j = i + 2; j < dnlen; j++) dn[j - 2] = dn[j];
dn[dnlen--] = _T('\0');
dn[dnlen--] = _T('\0');
i--;
--*newlen;
}
}
}
/* Must end with two NULLs. */
*newlen += 2;
*unformatted = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *newlen * sizeof(TCHAR));
if (! *unformatted) return 1;
for (i = 0, j = 0; i < dnlen; i++) {
if (dn[i] == _T('\r')) continue;
if (dn[i] == _T('\n')) (*unformatted)[j] = _T('\0');
else (*unformatted)[j] = dn[i];
j++;
}
return 0;
}
void override_milliseconds(TCHAR *service_name, HKEY key, TCHAR *value, unsigned long *buffer, unsigned long default_value, unsigned long event) {
unsigned long type = REG_DWORD;
unsigned long buflen = sizeof(unsigned long);
bool ok = false;
unsigned long ret = RegQueryValueEx(key, value, 0, &type, (unsigned char *) buffer, &buflen);
if (ret != ERROR_SUCCESS) {
if (ret != ERROR_FILE_NOT_FOUND) {
if (type != REG_DWORD) {
TCHAR milliseconds[16];
_sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), default_value);
log_event(EVENTLOG_WARNING_TYPE, event, service_name, value, milliseconds, 0);
}
else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, value, error_string(GetLastError()), 0);
}
}
else ok = true;
if (! ok) *buffer = default_value;
}
HKEY open_registry(const TCHAR *service_name, const TCHAR *sub, REGSAM sam) {
/* Get registry */
TCHAR registry[KEY_LENGTH];
HKEY key;
int ret;
if (sub) ret = _sntprintf_s(registry, _countof(registry), _TRUNCATE, NSSM_REGISTRY _T("\\%s"), service_name, sub);
else ret = _sntprintf_s(registry, _countof(registry), _TRUNCATE, NSSM_REGISTRY, service_name);
if (ret < 0) {
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("NSSM_REGISTRY"), _T("open_registry()"), 0);
return 0;
}
if (sam & KEY_WRITE) {
if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, registry, 0, 0, REG_OPTION_NON_VOLATILE, sam, 0, &key, 0) != ERROR_SUCCESS) {
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
return 0;
}
}
else {
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, registry, 0, sam, &key) != ERROR_SUCCESS) {
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENKEY_FAILED, registry, error_string(GetLastError()), 0);
return 0;
}
}
return key;
}
HKEY open_registry(const TCHAR *service_name, REGSAM sam) {
return open_registry(service_name, 0, sam);
}
int get_io_parameters(nssm_service_t *service, HKEY key) {
/* stdin */
if (get_createfile_parameters(key, NSSM_REG_STDIN, service->stdin_path, &service->stdin_sharing, NSSM_STDIN_SHARING, &service->stdin_disposition, NSSM_STDIN_DISPOSITION, &service->stdin_flags, NSSM_STDIN_FLAGS)) {
service->stdin_sharing = service->stdin_disposition = service->stdin_flags = 0;
ZeroMemory(service->stdin_path, _countof(service->stdin_path) * sizeof(TCHAR));
return 1;
}
/* stdout */
if (get_createfile_parameters(key, NSSM_REG_STDOUT, service->stdout_path, &service->stdout_sharing, NSSM_STDOUT_SHARING, &service->stdout_disposition, NSSM_STDOUT_DISPOSITION, &service->stdout_flags, NSSM_STDOUT_FLAGS)) {
service->stdout_sharing = service->stdout_disposition = service->stdout_flags = 0;
ZeroMemory(service->stdout_path, _countof(service->stdout_path) * sizeof(TCHAR));
return 2;
}
/* stderr */
if (get_createfile_parameters(key, NSSM_REG_STDERR, service->stderr_path, &service->stderr_sharing, NSSM_STDERR_SHARING, &service->stderr_disposition, NSSM_STDERR_DISPOSITION, &service->stderr_flags, NSSM_STDERR_FLAGS)) {
service->stderr_sharing = service->stderr_disposition = service->stderr_flags = 0;
ZeroMemory(service->stderr_path, _countof(service->stderr_path) * sizeof(TCHAR));
return 3;
}
return 0;
}
int get_parameters(nssm_service_t *service, STARTUPINFO *si) {
unsigned long ret;
/* Try to open the registry */
HKEY key = open_registry(service->name, KEY_READ);
if (! key) return 1;
/* Don't expand parameters when retrieving for the GUI. */
bool expand = si ? true : false;
/* Try to get executable file - MUST succeed */
if (get_string(key, NSSM_REG_EXE, service->exe, sizeof(service->exe), expand, false, true)) {
RegCloseKey(key);
return 3;
}
/* Try to get flags - may fail and we don't care */
if (get_string(key, NSSM_REG_FLAGS, service->flags, sizeof(service->flags), expand, false, true)) {
log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_FLAGS, NSSM_REG_FLAGS, service->name, service->exe, 0);
ZeroMemory(service->flags, sizeof(service->flags));
}
/* Try to get startup directory - may fail and we fall back to a default */
if (get_string(key, NSSM_REG_DIR, service->dir, sizeof(service->dir), expand, true, true) || ! service->dir[0]) {
_sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);
strip_basename(service->dir);
if (service->dir[0] == _T('\0')) {
/* Help! */
ret = GetWindowsDirectory(service->dir, sizeof(service->dir));
if (! ret || ret > sizeof(service->dir)) {
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_NO_DIR_AND_NO_FALLBACK, NSSM_REG_DIR, service->name, 0);
RegCloseKey(key);
return 4;
}
}
log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_DIR, NSSM_REG_DIR, service->name, service->dir, 0);
}
/* Try to get processor affinity - may fail. */
TCHAR buffer[512];
if (get_string(key, NSSM_REG_AFFINITY, buffer, sizeof(buffer), false, false, false) || ! buffer[0]) service->affinity = 0LL;
else if (affinity_string_to_mask(buffer, &service->affinity)) {
log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_AFFINITY_MASK, service->name, buffer);
service->affinity = 0LL;
}
else {
DWORD_PTR affinity, system_affinity;
if (GetProcessAffinityMask(GetCurrentProcess(), &affinity, &system_affinity)) {
_int64 effective_affinity = service->affinity & system_affinity;
if (effective_affinity != service->affinity) {
TCHAR *system = 0;
if (! affinity_mask_to_string(system_affinity, &system)) {
TCHAR *effective = 0;
if (! affinity_mask_to_string(effective_affinity, &effective)) {
log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_EFFECTIVE_AFFINITY_MASK, service->name, buffer, system, effective, 0);
}
HeapFree(GetProcessHeap(), 0, effective);
}
HeapFree(GetProcessHeap(), 0, system);
}
}
}
/* Try to get environment variables - may fail */
get_environment(service->name, key, NSSM_REG_ENV, &service->env, &service->envlen);
/* Environment variables to add to existing rather than replace - may fail. */
get_environment(service->name, key, NSSM_REG_ENV_EXTRA, &service->env_extra, &service->env_extralen);
/* Try to get priority - may fail. */
unsigned long priority;
if (get_number(key, NSSM_REG_PRIORITY, &priority, false) == 1) {
if (priority == (priority & priority_mask())) service->priority = priority;
else log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_PRIORITY, service->name, NSSM_REG_PRIORITY, 0);
}
/* Try to get file rotation settings - may fail. */
unsigned long rotate_files;
if (get_number(key, NSSM_REG_ROTATE, &rotate_files, false) == 1) {
if (rotate_files) service->rotate_files = true;
else service->rotate_files = false;
}
else service->rotate_files = false;
if (get_number(key, NSSM_REG_ROTATE_ONLINE, &rotate_files, false) == 1) {
if (rotate_files) service->rotate_stdout_online = service->rotate_stderr_online = true;
else service->rotate_stdout_online = service->rotate_stderr_online = false;
}
else service->rotate_stdout_online = service->rotate_stderr_online = false;
if (get_number(key, NSSM_REG_ROTATE_SECONDS, &service->rotate_seconds, false) != 1) service->rotate_seconds = 0;
if (get_number(key, NSSM_REG_ROTATE_BYTES_LOW, &service->rotate_bytes_low, false) != 1) service->rotate_bytes_low = 0;
if (get_number(key, NSSM_REG_ROTATE_BYTES_HIGH, &service->rotate_bytes_high, false) != 1) service->rotate_bytes_high = 0;
/* Try to get force new console setting - may fail. */
if (get_number(key, NSSM_REG_NO_CONSOLE, &service->no_console, false) != 1) service->no_console = 0;
/* Change to startup directory in case stdout/stderr are relative paths. */
TCHAR cwd[PATH_LENGTH];
GetCurrentDirectory(_countof(cwd), cwd);
SetCurrentDirectory(service->dir);
/* Try to get stdout and stderr */
if (get_io_parameters(service, key)) {
log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);
RegCloseKey(key);
SetCurrentDirectory(cwd);
return 5;
}
/* Change back in case the startup directory needs to be deleted. */
SetCurrentDirectory(cwd);
/* Try to get mandatory restart delay */
override_milliseconds(service->name, key, NSSM_REG_RESTART_DELAY, &service->restart_delay, 0, NSSM_EVENT_BOGUS_RESTART_DELAY);
/* Try to get throttle restart delay */
override_milliseconds(service->name, key, NSSM_REG_THROTTLE, &service->throttle_delay, NSSM_RESET_THROTTLE_RESTART, NSSM_EVENT_BOGUS_THROTTLE);
/* Try to get service stop flags. */
unsigned long type = REG_DWORD;
unsigned long stop_method_skip;
unsigned long buflen = sizeof(stop_method_skip);
bool stop_ok = false;
ret = RegQueryValueEx(key, NSSM_REG_STOP_METHOD_SKIP, 0, &type, (unsigned char *) &stop_method_skip, &buflen);
if (ret != ERROR_SUCCESS) {
if (ret != ERROR_FILE_NOT_FOUND) {
if (type != REG_DWORD) {
log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_STOP_METHOD_SKIP, service->name, NSSM_REG_STOP_METHOD_SKIP, NSSM, 0);
}
else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, NSSM_REG_STOP_METHOD_SKIP, error_string(GetLastError()), 0);
}
}
else stop_ok = true;
/* Try all methods except those requested to be skipped. */
service->stop_method = ~0;
if (stop_ok) service->stop_method &= ~stop_method_skip;
/* Try to get kill delays - may fail. */
override_milliseconds(service->name, key, NSSM_REG_KILL_CONSOLE_GRACE_PERIOD, &service->kill_console_delay, NSSM_KILL_CONSOLE_GRACE_PERIOD, NSSM_EVENT_BOGUS_KILL_CONSOLE_GRACE_PERIOD);
override_milliseconds(service->name, key, NSSM_REG_KILL_WINDOW_GRACE_PERIOD, &service->kill_window_delay, NSSM_KILL_WINDOW_GRACE_PERIOD, NSSM_EVENT_BOGUS_KILL_WINDOW_GRACE_PERIOD);
override_milliseconds(service->name, key, NSSM_REG_KILL_THREADS_GRACE_PERIOD, &service->kill_threads_delay, NSSM_KILL_THREADS_GRACE_PERIOD, NSSM_EVENT_BOGUS_KILL_THREADS_GRACE_PERIOD);
/* Try to get default exit action. */
bool default_action;
service->default_exit_action = NSSM_EXIT_RESTART;
TCHAR action_string[ACTION_LEN];
if (! get_exit_action(service->name, 0, action_string, &default_action)) {
for (int i = 0; exit_action_strings[i]; i++) {
if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
service->default_exit_action = i;
break;
}
}
}
/* Close registry */
RegCloseKey(key);
return 0;
}
/*
Sets the string for the exit action corresponding to the exit code.
ret is a pointer to an unsigned long containing the exit code.
If ret is NULL, we retrieve the default exit action unconditionally.
action is a buffer which receives the string.
default_action is a pointer to a bool which is set to false if there
was an explicit string for the given exit code, or true if we are
returning the default action.
Returns: 0 on success.
1 on error.
*/
int get_exit_action(const TCHAR *service_name, unsigned long *ret, TCHAR *action, bool *default_action) {
/* Are we returning the default action or a status-specific one? */
*default_action = ! ret;
/* Try to open the registry */
HKEY key = open_registry(service_name, NSSM_REG_EXIT, KEY_READ);
if (! key) return 1;
unsigned long type = REG_SZ;
unsigned long action_len = ACTION_LEN;
TCHAR code[16];
if (! ret) code[0] = _T('\0');
else if (_sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), *ret) < 0) {
RegCloseKey(key);
return get_exit_action(service_name, 0, action, default_action);
}
if (RegQueryValueEx(key, code, 0, &type, (unsigned char *) action, &action_len) != ERROR_SUCCESS) {
RegCloseKey(key);
/* Try again with * as the key if an exit code was defined */
if (ret) return get_exit_action(service_name, 0, action, default_action);
return 0;
}
/* Close registry */
RegCloseKey(key);
return 0;
}

@ -0,0 +1,58 @@
#ifndef REGISTRY_H
#define REGISTRY_H
#define NSSM_REGISTRY _T("SYSTEM\\CurrentControlSet\\Services\\%s\\Parameters")
#define NSSM_REGISTRY_GROUPS _T("SYSTEM\\CurrentControlSet\\Control\\ServiceGroupOrder")
#define NSSM_REG_GROUPS _T("List")
#define NSSM_REG_EXE _T("Application")
#define NSSM_REG_FLAGS _T("AppParameters")
#define NSSM_REG_DIR _T("AppDirectory")
#define NSSM_REG_ENV _T("AppEnvironment")
#define NSSM_REG_ENV_EXTRA _T("AppEnvironmentExtra")
#define NSSM_REG_EXIT _T("AppExit")
#define NSSM_REG_RESTART_DELAY _T("AppRestartDelay")
#define NSSM_REG_THROTTLE _T("AppThrottle")
#define NSSM_REG_STOP_METHOD_SKIP _T("AppStopMethodSkip")
#define NSSM_REG_KILL_CONSOLE_GRACE_PERIOD _T("AppStopMethodConsole")
#define NSSM_REG_KILL_WINDOW_GRACE_PERIOD _T("AppStopMethodWindow")
#define NSSM_REG_KILL_THREADS_GRACE_PERIOD _T("AppStopMethodThreads")
#define NSSM_REG_STDIN _T("AppStdin")
#define NSSM_REG_STDOUT _T("AppStdout")
#define NSSM_REG_STDERR _T("AppStderr")
#define NSSM_REG_STDIO_SHARING _T("ShareMode")
#define NSSM_REG_STDIO_DISPOSITION _T("CreationDisposition")
#define NSSM_REG_STDIO_FLAGS _T("FlagsAndAttributes")
#define NSSM_REG_ROTATE _T("AppRotateFiles")
#define NSSM_REG_ROTATE_ONLINE _T("AppRotateOnline")
#define NSSM_REG_ROTATE_SECONDS _T("AppRotateSeconds")
#define NSSM_REG_ROTATE_BYTES_LOW _T("AppRotateBytes")
#define NSSM_REG_ROTATE_BYTES_HIGH _T("AppRotateBytesHigh")
#define NSSM_REG_PRIORITY _T("AppPriority")
#define NSSM_REG_AFFINITY _T("AppAffinity")
#define NSSM_REG_NO_CONSOLE _T("AppNoConsole")
#define NSSM_STDIO_LENGTH 29
HKEY open_registry(const TCHAR *, const TCHAR *, REGSAM sam);
HKEY open_registry(const TCHAR *, REGSAM sam);
int create_messages();
int create_parameters(nssm_service_t *, bool);
int create_exit_action(TCHAR *, const TCHAR *, bool);
int get_environment(TCHAR *, HKEY, TCHAR *, TCHAR **, unsigned long *);
int get_string(HKEY, TCHAR *, TCHAR *, unsigned long, bool, bool, bool);
int get_string(HKEY, TCHAR *, TCHAR *, unsigned long, bool);
int expand_parameter(HKEY, TCHAR *, TCHAR *, unsigned long, bool, bool);
int expand_parameter(HKEY, TCHAR *, TCHAR *, unsigned long, bool);
int set_string(HKEY, TCHAR *, TCHAR *, bool);
int set_string(HKEY, TCHAR *, TCHAR *);
int set_expand_string(HKEY, TCHAR *, TCHAR *);
int set_number(HKEY, TCHAR *, unsigned long);
int get_number(HKEY, TCHAR *, unsigned long *, bool);
int get_number(HKEY, TCHAR *, unsigned long *);
int format_double_null(TCHAR *, unsigned long, TCHAR **, unsigned long *);
int unformat_double_null(TCHAR *, unsigned long, TCHAR **, unsigned long *);
void override_milliseconds(TCHAR *, HKEY, TCHAR *, unsigned long *, unsigned long, unsigned long);
int get_io_parameters(nssm_service_t *, HKEY);
int get_parameters(nssm_service_t *, STARTUPINFO *);
int get_exit_action(const TCHAR *, unsigned long *, TCHAR *, bool *);
#endif

@ -0,0 +1,78 @@
//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by nssm.rc
//
#define IDC_STATIC (-1)
#define IDI_NSSM 101
#define IDD_INSTALL 102
#define IDD_REMOVE 103
#define IDD_EDIT 104
#define IDD_APPLICATION 105
#define IDD_DETAILS 106
#define IDD_LOGON 107
#define IDD_IO 108
#define IDD_ROTATION 109
#define IDD_APPEXIT 110
#define IDD_SHUTDOWN 111
#define IDD_ENVIRONMENT 112
#define IDD_NATIVE 113
#define IDD_PROCESS 114
#define IDD_DEPENDENCIES 115
#define IDC_PATH 1000
#define IDC_TAB1 1001
#define IDC_CANCEL 1002
#define IDC_BROWSE 1003
#define IDC_FLAGS 1004
#define IDC_NAME 1005
#define IDC_REMOVE 1007
#define IDC_METHOD_CONSOLE 1008
#define IDC_METHOD_WINDOW 1009
#define IDC_METHOD_THREADS 1010
#define IDC_METHOD_TERMINATE 1011
#define IDC_KILL_CONSOLE 1012
#define IDC_KILL_WINDOW 1013
#define IDC_KILL_THREADS 1014
#define IDC_STDIN 1015
#define IDC_STDOUT 1016
#define IDC_STDERR 1017
#define IDC_BROWSE_STDIN 1018
#define IDC_BROWSE_STDOUT 1019
#define IDC_BROWSE_STDERR 1020
#define IDC_THROTTLE 1021
#define IDC_APPEXIT 1022
#define IDC_RESTART_DELAY 1023
#define IDC_DIR 1024
#define IDC_BROWSE_DIR 1025
#define IDC_ENVIRONMENT 1026
#define IDC_ENVIRONMENT_REPLACE 1027
#define IDC_TRUNCATE 1028
#define IDC_ROTATE 1029
#define IDC_ROTATE_ONLINE 1030
#define IDC_ROTATE_SECONDS 1031
#define IDC_ROTATE_BYTES_LOW 1032
#define IDC_DISPLAYNAME 1033
#define IDC_DESCRIPTION 1034
#define IDC_STARTUP 1035
#define IDC_LOCALSYSTEM 1036
#define IDC_INTERACT 1037
#define IDC_ACCOUNT 1038
#define IDC_USERNAME 1039
#define IDC_PASSWORD1 1040
#define IDC_PASSWORD2 1041
#define IDC_PRIORITY 1042
#define IDC_AFFINITY_ALL 1043
#define IDC_AFFINITY 1044
#define IDC_CONSOLE 1045
#define IDC_DEPENDENCIES 1046
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 115
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1047
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

File diff suppressed because it is too large Load Diff

@ -0,0 +1,146 @@
#ifndef SERVICE_H
#define SERVICE_H
/*
MSDN says the commandline in CreateProcess() is limited to 32768 characters
and the application name to MAX_PATH.
A service name and service display name are limited to 256 characters.
A registry key is limited to 255 characters.
A registry value is limited to 16383 characters.
Therefore we limit the service name to accommodate the path under HKLM.
*/
#define EXE_LENGTH PATH_LENGTH
#define CMD_LENGTH 32768
#define KEY_LENGTH 255
#define VALUE_LENGTH 16383
#define SERVICE_NAME_LENGTH 256
#define ACTION_LEN 16
#define NSSM_KERNEL_DRIVER _T("SERVICE_KERNEL_DRIVER")
#define NSSM_FILE_SYSTEM_DRIVER _T("SERVICE_FILE_SYSTEM_DRIVER")
#define NSSM_WIN32_OWN_PROCESS _T("SERVICE_WIN32_OWN_PROCESS")
#define NSSM_WIN32_SHARE_PROCESS _T("SERVICE_WIN32_SHARE_PROCESS")
#define NSSM_INTERACTIVE_PROCESS _T("SERVICE_INTERACTIVE_PROCESS")
#define NSSM_SHARE_INTERACTIVE_PROCESS NSSM_WIN32_SHARE_PROCESS _T("|") NSSM_INTERACTIVE_PROCESS
#define NSSM_UNKNOWN _T("?")
#define NSSM_ROTATE_OFFLINE 0
#define NSSM_ROTATE_ONLINE 1
#define NSSM_ROTATE_ONLINE_ASAP 2
typedef struct {
bool native;
TCHAR name[SERVICE_NAME_LENGTH];
TCHAR displayname[SERVICE_NAME_LENGTH];
TCHAR description[VALUE_LENGTH];
unsigned long startup;
TCHAR *username;
size_t usernamelen;
TCHAR *password;
size_t passwordlen;
unsigned long type;
TCHAR image[PATH_LENGTH];
TCHAR exe[EXE_LENGTH];
TCHAR flags[VALUE_LENGTH];
TCHAR dir[DIR_LENGTH];
TCHAR *env;
__int64 affinity;
TCHAR *dependencies;
unsigned long dependencieslen;
unsigned long envlen;
TCHAR *env_extra;
unsigned long env_extralen;
unsigned long priority;
unsigned long no_console;
TCHAR stdin_path[PATH_LENGTH];
unsigned long stdin_sharing;
unsigned long stdin_disposition;
unsigned long stdin_flags;
TCHAR stdout_path[PATH_LENGTH];
unsigned long stdout_sharing;
unsigned long stdout_disposition;
unsigned long stdout_flags;
HANDLE stdout_pipe;
HANDLE stdout_thread;
unsigned long stdout_tid;
TCHAR stderr_path[PATH_LENGTH];
unsigned long stderr_sharing;
unsigned long stderr_disposition;
unsigned long stderr_flags;
HANDLE stderr_pipe;
HANDLE stderr_thread;
unsigned long stderr_tid;
bool rotate_files;
unsigned long rotate_stdout_online;
unsigned long rotate_stderr_online;
unsigned long rotate_seconds;
unsigned long rotate_bytes_low;
unsigned long rotate_bytes_high;
unsigned long default_exit_action;
unsigned long restart_delay;
unsigned long throttle_delay;
unsigned long stop_method;
unsigned long kill_console_delay;
unsigned long kill_window_delay;
unsigned long kill_threads_delay;
SC_HANDLE handle;
SERVICE_STATUS status;
SERVICE_STATUS_HANDLE status_handle;
HANDLE process_handle;
unsigned long pid;
HANDLE wait_handle;
bool stopping;
bool allow_restart;
unsigned long throttle;
CRITICAL_SECTION throttle_section;
bool throttle_section_initialised;
CONDITION_VARIABLE throttle_condition;
HANDLE throttle_timer;
LARGE_INTEGER throttle_duetime;
FILETIME creation_time;
FILETIME exit_time;
TCHAR *initial_env;
} nssm_service_t;
void WINAPI service_main(unsigned long, TCHAR **);
TCHAR *service_control_text(unsigned long);
TCHAR *service_status_text(unsigned long);
void log_service_control(TCHAR *, unsigned long, bool);
unsigned long WINAPI service_control_handler(unsigned long, unsigned long, void *, void *);
int affinity_mask_to_string(__int64, TCHAR **);
int affinity_string_to_mask(TCHAR *, __int64 *);
unsigned long priority_mask();
int priority_constant_to_index(unsigned long);
unsigned long priority_index_to_constant(int);
nssm_service_t *alloc_nssm_service();
void set_nssm_service_defaults(nssm_service_t *);
void cleanup_nssm_service(nssm_service_t *);
SC_HANDLE open_service_manager(unsigned long);
SC_HANDLE open_service(SC_HANDLE, TCHAR *, unsigned long, TCHAR *, unsigned long);
QUERY_SERVICE_CONFIG *query_service_config(const TCHAR *, SC_HANDLE);
int set_service_dependencies(const TCHAR *, SC_HANDLE, TCHAR *);
int get_service_dependencies(const TCHAR *, SC_HANDLE, TCHAR **, unsigned long *, int);
int get_service_dependencies(const TCHAR *, SC_HANDLE, TCHAR **, unsigned long *);
int set_service_description(const TCHAR *, SC_HANDLE, TCHAR *);
int get_service_description(const TCHAR *, SC_HANDLE, unsigned long, TCHAR *);
int get_service_startup(const TCHAR *, SC_HANDLE, const QUERY_SERVICE_CONFIG *, unsigned long *);
int get_service_username(const TCHAR *, const QUERY_SERVICE_CONFIG *, TCHAR **, size_t *);
int pre_install_service(int, TCHAR **);
int pre_remove_service(int, TCHAR **);
int pre_edit_service(int, TCHAR **);
int install_service(nssm_service_t *);
int remove_service(nssm_service_t *);
int edit_service(nssm_service_t *, bool);
int control_service(unsigned long, int, TCHAR **);
void set_service_recovery(nssm_service_t *);
int monitor_service(nssm_service_t *);
int start_service(nssm_service_t *);
int stop_service(nssm_service_t *, unsigned long, bool, bool);
void CALLBACK end_service(void *, unsigned char);
void throttle_restart(nssm_service_t *);
int await_shutdown(nssm_service_t *, TCHAR *, unsigned long);
#endif

File diff suppressed because it is too large Load Diff

@ -0,0 +1,47 @@
#ifndef SETTINGS_H
#define SETTINGS_H
#define NSSM_NATIVE_DEPENDONGROUP _T("DependOnGroup")
#define NSSM_NATIVE_DEPENDONSERVICE _T("DependOnService")
#define NSSM_NATIVE_DESCRIPTION _T("Description")
#define NSSM_NATIVE_DISPLAYNAME _T("DisplayName")
#define NSSM_NATIVE_IMAGEPATH _T("ImagePath")
#define NSSM_NATIVE_NAME _T("Name")
#define NSSM_NATIVE_OBJECTNAME _T("ObjectName")
#define NSSM_NATIVE_STARTUP _T("Start")
#define NSSM_NATIVE_TYPE _T("Type")
/* Are additional arguments needed? */
#define ADDITIONAL_GETTING (1 << 0)
#define ADDITIONAL_SETTING (1 << 1)
#define ADDITIONAL_RESETTING (1 << 2)
#define ADDITIONAL_CRLF (1 << 3)
#define ADDITIONAL_MANDATORY ADDITIONAL_GETTING|ADDITIONAL_SETTING|ADDITIONAL_RESETTING
#define DEPENDENCY_SERVICES (1 << 0)
#define DEPENDENCY_GROUPS (1 << 1)
#define DEPENDENCY_ALL (DEPENDENCY_SERVICES|DEPENDENCY_GROUPS)
typedef union {
unsigned long numeric;
TCHAR *string;
} value_t;
typedef int (*setting_function_t)(const TCHAR *, void *, const TCHAR *, void *, value_t *, const TCHAR *);
typedef struct {
const TCHAR *name;
unsigned long type;
void *default_value;
bool native;
int additional;
setting_function_t set;
setting_function_t get;
} settings_t;
int set_setting(const TCHAR *, HKEY, settings_t *, value_t *, const TCHAR *);
int set_setting(const TCHAR *, SC_HANDLE, settings_t *, value_t *, const TCHAR *);
int get_setting(const TCHAR *, HKEY, settings_t *, value_t *, const TCHAR *);
int get_setting(const TCHAR *, SC_HANDLE, settings_t *, value_t *, const TCHAR *);
#endif

@ -0,0 +1,49 @@
@rem Set default version in case git isn't available.
set description=0.0-0-prerelease
@rem Get canonical version from git tags, eg v2.21-24-g2c60e53.
for /f %%v in ('git describe --tags --long') do set description=%%v
@rem Strip leading v if present, eg 2.21-24-g2c60e53.
set description=%description:v=%
set version=%description%
@rem Get the number of commits and commit hash, eg 24-g2c60e53.
set n=%version:*-=%
set commit=%n:*-=%
call set n=%%n:%commit%=%%
set n=%n:~0,-1%
@rem Strip n and commit, eg 2.21.
call set version=%%version:%n%-%commit%=%%
set version=%version:~0,-1%
@rem Find major and minor.
set minor=%version:*.=%
call set major=%%version:.%minor%=%%
@rem Build flags.
set flags=0L
@rem Don't include n and commit if we match a tag exactly.
if "%n%" == "0" (set description=%major%.%minor%) else set flags=VS_FF_PRERELEASE
@rem Maybe we couldn't get the git tag.
if "%commit%" == "prerelease" set flags=VS_FF_PRERELEASE
@rem Ignore the build number if this isn't Jenkins.
if "%BUILD_NUMBER%" == "" set BUILD_NUMBER=0
@rem Copyright year provided by Jenkins.
set md=%BUILD_ID:*-=%
call set year=%%BUILD_ID:%md%=%%
set year=%year:~0,-1%
if "%BUILD_ID%" == "" set year=
@rem Create version.h.
@echo>version.h.new #define NSSM_VERSION _T("%description%")
@echo>>version.h.new #define NSSM_VERSIONINFO %major%,%minor%,%n%,%BUILD_NUMBER%
@echo>>version.h.new #define NSSM_DATE _T("%DATE%")
@echo>>version.h.new #define NSSM_FILEFLAGS %flags%
@echo>>version.h.new #define NSSM_COPYRIGHT _T("Public Domain; Author Iain Patterson 2003-%year%")
fc version.h version.h.new >NUL: 2>NUL:
if %ERRORLEVEL% == 0 (del version.h.new) else (move /y version.h.new version.h)

@ -19,10 +19,10 @@ def parseDate(date_string: str):
class HistoryDBManage():
token = 'k2_-WhuPSNlncv4ftpsFbiMUwt2XeQOtZHDgtx2jaRF1b6_yFCFYenvjq0v6h2ib1346rfEYYijmfBzkgt4OMA=='
token = 'bWcPFA8o3CHoWi9dhJJxBHmD6lrpJ0KmzMA06FjAAGzktwwyreVF6dBCL9G2oonXzQdIUQcuMJyCe_TMwM-0XA=='
org = "DCS"
# bucket = "history"
url = "http://localhost:8086"
url = "http://localhost:6324"
def __init__(self, bucket, mem = None):
self.client = InfluxDBClient(url = self.url, token = self.token, org = self.org)

Loading…
Cancel
Save