diff --git a/.gitignore b/.gitignore index 304bf1e..bf55b1d 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ o *.spec Static/license.lic .idea/.gitignore +/Static/influxDB/engine/ diff --git a/Static/influxDB/config.yaml b/Static/influxDB/config.yaml new file mode 100644 index 0000000..d25284c --- /dev/null +++ b/Static/influxDB/config.yaml @@ -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" \ No newline at end of file diff --git a/Static/influxDB/influxDB-service.exe b/Static/influxDB/influxDB-service.exe deleted file mode 100644 index ba27e3f..0000000 Binary files a/Static/influxDB/influxDB-service.exe and /dev/null differ diff --git a/Static/influxDB/nssm-2.24/ChangeLog.txt b/Static/influxDB/nssm-2.24/ChangeLog.txt new file mode 100644 index 0000000..ea2b5ff --- /dev/null +++ b/Static/influxDB/nssm-2.24/ChangeLog.txt @@ -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\\Parameters\AppExit\, + falling back to the unnamed value if no such code is listed. Parse the + (string) value of this entry as follows: + + Restart: Start the application again (NSSM default). + Ignore: Do nothing (srvany default). + Exit: Stop the service. + +Changes since 2.0 +----------------- + * Added support for building a 64-bit executable. + + * Added project files for newer versions of Visual Studio. diff --git a/Static/influxDB/nssm-2.24/README.txt b/Static/influxDB/nssm-2.24/README.txt new file mode 100644 index 0000000..11fbac1 --- /dev/null +++ b/Static/influxDB/nssm-2.24/README.txt @@ -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. means you must insert the +appropriate string and [] means the string is optional. See the +examples below... + +Note that everywhere appears you may substitute the +service's display name. + + +Installation using the GUI +-------------------------- +To install a service, run + + nssm install + +You will be prompted to enter the full path to the application you wish +to run and any command line options to pass to that application. + +Use the system service manager (services.msc) to control advanced service +properties such as startup method and desktop interaction. NSSM may +support these options at a later time... + + +Installation using the command line +----------------------------------- +To install a service, run + + nssm install [] + +NSSM will then attempt to install a service which runs the named application +with the given options (if you specified any). + +Don't forget to enclose paths in "quotes" if they contain spaces! + +If you want to include quotes in the options you will need to """quote""" the +quotes. + + +Managing the service +-------------------- +NSSM will launch the application listed in the registry when you send it a +start signal and will terminate it when you send a stop signal. So far, so +much like srvany. But NSSM is the Non-Sucking service manager and can take +action if/when the application dies. + +With no configuration from you, NSSM will try to restart itself if it notices +that the application died but you didn't send it a stop signal. NSSM will +keep trying, pausing between each attempt, until the service is successfully +started or you send it a stop signal. + +NSSM will pause an increasingly longer time between subsequent restart attempts +if the service fails to start in a timely manner, up to a maximum of four +minutes. This is so it does not consume an excessive amount of CPU time trying +to start a failed application over and over again. If you identify the cause +of the failure and don't want to wait you can use the Windows service console +(where the service will be shown in Paused state) to send a continue signal to +NSSM and it will retry within a few seconds. + +By default, NSSM defines "a timely manner" to be within 1500 milliseconds. +You can change the threshold for the service by setting the number of +milliseconds as a REG_DWORD value in the registry at +HKLM\SYSTEM\CurrentControlSet\Services\\Parameters\AppThrottle. + +Alternatively, NSSM can pause for a configurable amount of time before +attempting to restart the application even if it successfully ran for the +amount of time specified by AppThrottle. NSSM will consult the REG_DWORD value +at HKLM\SYSTEM\CurrentControlSet\Services\\Parameters\AppRestartDelay +for the number of milliseconds to wait before attempting a restart. If +AppRestartDelay is set and the application is determined to be subject to +throttling, NSSM will pause the service for whichever is longer of the +configured restart delay and the calculated throttle period. + +If AppRestartDelay is missing or invalid, only throttling will be applied. + +NSSM will look in the registry under +HKLM\SYSTEM\CurrentControlSet\Services\\Parameters\AppExit for +string (REG_EXPAND_SZ) values corresponding to the exit code of the application. +If the application exited with code 1, for instance, NSSM will look for a +string value under AppExit called "1" or, if it does not find it, will +fall back to the AppExit (Default) value. You can find out the exit code +for the application by consulting the system event log. NSSM will log the +exit code when the application exits. + +Based on the data found in the registry, NSSM will take one of three actions: + +If the value data is "Restart" NSSM will try to restart the application as +described above. This is its default behaviour. + +If the value data is "Ignore" NSSM will not try to restart the application +but will continue running itself. This emulates the (usually undesirable) +behaviour of srvany. The Windows Services console would show the service +as still running even though the application has exited. + +If the value data is "Exit" NSSM will exit gracefully. The Windows Services +console would show the service as stopped. If you wish to provide +finer-grained control over service recovery you should use this code and +edit the failure action manually. Please note that Windows versions prior +to Vista will not consider such an exit to be a failure. On older versions +of Windows you should use "Suicide" instead. + +If the value data is "Suicide" NSSM will simulate a crash and exit without +informing the service manager. This option should only be used for +pre-Vista systems where you wish to apply a service recovery action. Note +that if the monitored application exits with code 0, NSSM will only honour a +request to suicide if you explicitly configure a registry key for exit code 0. +If only the default action is set to Suicide NSSM will instead exit gracefully. + + +Application priority +-------------------- +NSSM can set the priority class of the managed application. NSSM will look in +the registry under HKLM\SYSTEM\CurrentControlSet\Services\\Parameters +for the REG_DWORD entry AppPriority. Valid values correspond to arguments to +SetPriorityClass(). If AppPriority() is missing or invalid the +application will be launched with normal priority. + + +Processor affinity +------------------ +NSSM can set the CPU affinity of the managed application. NSSM will look in +the registry under HKLM\SYSTEM\CurrentControlSet\Services\\Parameters +for the REG_SZ entry AppAffinity. It should specify a comma-separated listed +of zero-indexed processor IDs. A range of processors may optionally be +specified with a dash. No other characters are allowed in the string. + +For example, to specify the first; second; third and fifth CPUs, an appropriate +AppAffinity would be 0-2,4. + +If AppAffinity is missing or invalid, NSSM will not attempt to restrict the +application to specific CPUs. + +Note that the 64-bit version of NSSM can configure a maximum of 64 CPUs in this +way and that the 32-bit version can configure a maxium of 32 CPUs even when +running on 64-bit Windows. + + +Stopping the service +-------------------- +When stopping a service NSSM will attempt several different methods of killing +the monitored application, each of which can be disabled if necessary. + +First NSSM will attempt to generate a Control-C event and send it to the +application's console. Batch scripts or console applications may intercept +the event and shut themselves down gracefully. GUI applications do not have +consoles and will not respond to this method. + +Secondly NSSM will enumerate all windows created by the application and send +them a WM_CLOSE message, requesting a graceful exit. + +Thirdly NSSM will enumerate all threads created by the application and send +them a WM_QUIT message, requesting a graceful exit. Not all applications' +threads have message queues; those which do not will not respond to this +method. + +Finally NSSM will call TerminateProcess() to request that the operating +system forcibly terminate the application. TerminateProcess() cannot be +trapped or ignored, so in most circumstances the application will be killed. +However, there is no guarantee that it will have a chance to perform any +tidyup operations before it exits. + +Any or all of the methods above may be disabled. NSSM will look for the +HKLM\SYSTEM\CurrentControlSet\Services\\Parameters\AppStopMethodSkip +registry value which should be of type REG_DWORD set to a bit field describing +which methods should not be applied. + + If AppStopMethodSkip includes 1, Control-C events will not be generated. + If AppStopMethodSkip includes 2, WM_CLOSE messages will not be posted. + If AppStopMethodSkip includes 4, WM_QUIT messages will not be posted. + If AppStopMethodSkip includes 8, TerminateProcess() will not be called. + +If, for example, you knew that an application did not respond to Control-C +events and did not have a thread message queue, you could set AppStopMethodSkip +to 5 and NSSM would not attempt to use those methods to stop the application. + +Take great care when including 8 in the value of AppStopMethodSkip. If NSSM +does not call TerminateProcess() it is possible that the application will not +exit when the service stops. + +By default NSSM will allow processes 1500ms to respond to each of the methods +described above before proceeding to the next one. The timeout can be +configured on a per-method basis by creating REG_DWORD entries in the +registry under HKLM\SYSTEM\CurrentControlSet\Services\\Parameters. + + AppStopMethodConsole + AppStopMethodWindow + AppStopMethodThreads + +Each value should be set to the number of milliseconds to wait. Please note +that the timeout applies to each process in the application's process tree, +so the actual time to shutdown may be longer than the sum of all configured +timeouts if the application spawns multiple subprocesses. + + +Console window +-------------- +By default, NSSM will create a console window so that applications which +are capable of reading user input can do so - subject to the service being +allowed to interact with the desktop. + +Creation of the console can be suppressed by setting the integer (REG_DWORD) +HKLM\SYSTEM\CurrentControlSet\Services\\Parameters\AppNoConsole +registry value to 1. + + +I/O redirection +--------------- +NSSM can redirect the managed application's I/O to any path capable of being +opened by CreateFile(). This enables, for example, capturing the log output +of an application which would otherwise only write to the console or accepting +input from a serial port. + +NSSM will look in the registry under +HKLM\SYSTEM\CurrentControlSet\Services\\Parameters for the keys +corresponding to arguments to CreateFile(). All are optional. If no path is +given for a particular stream it will not be redirected. If a path is given +but any of the other values are omitted they will be receive sensible defaults. + + AppStdin: Path to receive input. + AppStdout: Path to receive output. + AppStderr: Path to receive error output. + +Parameters for CreateFile() are providing with the "AppStdinShareMode", +"AppStdinCreationDisposition" and "AppStdinFlagsAndAttributes" values (and +analogously for stdout and stderr). + +In general, if you want the service to log its output, set AppStdout and +AppStderr to the same path, eg C:\Users\Public\service.log, and it should +work. Remember, however, that the path must be accessible to the user +running the service. + + +File rotation +------------- +When using I/O redirection, NSSM can rotate existing output files prior to +opening stdout and/or stderr. An existing file will be renamed with a +suffix based on the file's last write time, to millisecond precision. For +example, the file nssm.log might be rotated to nssm-20131221T113939.457.log. + +NSSM will look in the registry under +HKLM\SYSTEM\CurrentControlSet\Services\\Parameters for REG_DWORD +entries which control how rotation happens. + +If AppRotateFiles is missing or set to 0, rotation is disabled. Any non-zero +value enables rotation. + +If AppRotateSeconds is non-zero, a file will not be rotated if its last write +time is less than the given number of seconds in the past. + +If AppRotateBytes is non-zero, a file will not be rotated if it is smaller +than the given number of bytes. 64-bit file sizes can be handled by setting +a non-zero value of AppRotateBytesHigh. + +Rotation is independent of the CreateFile() parameters used to open the files. +They will be rotated regardless of whether NSSM would otherwise have appended +or replaced them. + +NSSM can also rotate files which hit the configured size threshold while the +service is running. Additionally, you can trigger an on-demand rotation by +running the command + + nssm rotate + +On-demand rotations will happen after the next line of data is read from +the managed application, regardless of the value of AppRotateBytes. Be aware +that if the application is not particularly verbose the rotation may not +happen for some time. + +To enable online and on-demand rotation, set AppRotateOnline to a non-zero +value. + +Note that online rotation requires NSSM to intercept the application's I/O +and create the output files on its behalf. This is more complex and +error-prone than simply redirecting the I/O streams before launching the +application. Therefore online rotation is not enabled by default. + + +Environment variables +--------------------- +NSSM can replace or append to the managed application's environment. Two +multi-valued string (REG_MULTI_SZ) registry values are recognised under +HKLM\SYSTEM\CurrentControlSet\Services\\Parameters. + +AppEnvironment defines a list of environment variables which will override +the service's environment. AppEnvironmentExtra defines a list of +environment variables which will be added to the service's environment. + +Each entry in the list should be of the form KEY=VALUE. It is possible to +omit the VALUE but the = symbol is mandatory. + +Environment variables listed in both AppEnvironment and AppEnvironmentExtra +are subject to normal expansion, so it is possible, for example, to update the +system path by setting "PATH=C:\bin;%PATH%" in AppEnvironmentExtra. Variables +are expanded in the order in which they appear, so if you want to include the +value of one variable in another variable you should declare the dependency +first. + +Because variables defined in AppEnvironment override the existing +environment it is not possible to refer to any variables which were previously +defined. + +For example, the following AppEnvironment block: + + PATH=C:\Windows\System32;C:\Windows + PATH=C:\bin;%PATH% + +Would result in a PATH of "C:\bin;C:\Windows\System32;C:\Windows" as expected. + +Whereas the following AppEnvironment block: + + PATH=C:\bin;%PATH% + +Would result in a path containing only C:\bin and probably cause the +application to fail to start. + +Most people will want to use AppEnvironmentExtra exclusively. srvany only +supports AppEnvironment. + + +Managing services using the GUI +------------------------------- +NSSM can edit the settings of existing services with the same GUI that is +used to install them. Run + + nssm edit + +to bring up the GUI. + +NSSM offers limited editing capabilities for services other than those which +run NSSM itself. When NSSM is asked to edit a service which does not have +the App* registry settings described above, the GUI will allow editing only +system settings such as the service display name and description. + + +Managing services using the command line +---------------------------------------- +NSSM can retrieve or set individual service parameters from the command line. +In general the syntax is as follows, though see below for exceptions. + + nssm get + + nssm set + +Parameters can also be reset to their default values. + + nssm reset + +The parameter names recognised by NSSM are the same as the registry entry +names described above, eg AppDirectory. + +NSSM offers limited editing capabilities for Services other than those which +run NSSM itself. The parameters recognised are as follows: + + Description: Service description. + DisplayName: Service display name. + ImagePath: Path to the service executable. + ObjectName: User account which runs the service. + Name: Service key name. + Start: Service startup type. + Type: Service type. + +These correspond to the registry values under the service's key +HKLM\SYSTEM\CurrentControlSet\Services\. + + +Note that NSSM will concatenate all arguments passed on the command line +with spaces to form the value to set. Thus the following two invocations +would have the same effect. + + nssm set Description "NSSM managed service" + + nssm set Description NSSM managed service + + +Non-standard parameters +----------------------- +The AppEnvironment 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 AppEnvironmentExtra + +whereas the syntax below will print only the value of the CLASSPATH +variable if it is configured in the environment block, or the empty string +if it is not configured. + + nssm get AppEnvironmentExtra CLASSPATH + +When setting an environment block, each variable should be specified as a +KEY=VALUE pair in separate command line arguments. For example: + + nssm set AppEnvironment CLASSPATH=C:\Classes TEMP=C:\Temp + + +The AppExit parameter requires an additional argument specifying the exit +code to get or set. The default action can be specified with the string +Default. + +For example, to get the default exit action for a service you should run + + nssm get AppExit Default + +To get the exit action when the application exits with exit code 2, run + + nssm get AppExit 2 + +Note that if no explicit action is configured for a specified exit code, +NSSM will print the default exit action. + +To set configure the service to stop when the application exits with an +exit code of 2, run + + nssm set AppExit 2 Exit + + +The AppPriority parameter is used to set the priority class of the +managed application. Valid priorities are as follows: + + REALTIME_PRIORITY_CLASS + HIGH_PRIORITY_CLASS + ABOVE_NORMAL_PRIORITY_CLASS + NORMAL_PRIORITY_CLASS + BELOW_NORMAL_PRIORITY_CLASS + IDLE_PRIORITY_CLASS + + +The DependOnGroup and DependOnService parameters are used to query or set +the dependencies for the service. When setting dependencies, each service +or service group (preceded with the + symbol) should be specified in +separate command line arguments. For example: + + nssm set DependOnService RpcSs LanmanWorkstation + + +The Name parameter can only be queried, not set. It returns the service's +registry key name. This may be useful to know if you take advantage of +the fact that you can substitute the service's display name anywhere where +the syntax calls for . + + +The ObjectName parameter requires an additional argument only when setting +a username. The additional argument is the password of the user. + +To retrieve the username, run + + nssm get ObjectName + +To set the username and password, run + + nssm set ObjectName + +Note that the rules of argument concatenation still apply. The following +invocation is valid and will have the expected effect. + + nssm set ObjectName correct horse battery staple + +The following well-known usernames do not need a password. The password +parameter can be omitted when using them: + + "LocalSystem" aka "System" aka "NT Authority\System" + "LocalService" aka "Local Service" aka "NT Authority\Local Service" + "NetworkService" aka "Network Service" aka "NT Authority\Network Service" + + +The Start parameter is used to query or set the startup type of the service. +Valid service startup types are as follows: + + SERVICE_AUTO_START: Automatic startup at boot. + SERVICE_DELAYED_START: Delayed startup at boot. + SERVICE_DEMAND_START: Manual service startup. + SERVICE_DISABLED: The service is disabled. + +Note that SERVICE_DELAYED_START is not supported on versions of Windows prior +to Vista. NSSM will set the service to automatic startup if delayed start is +unavailable. + + +The Type parameter is used to query or set the service type. NSSM recognises +all currently documented service types but will only allow setting one of two +types: + + SERVICE_WIN32_OWN_PROCESS: A standalone service. This is the default. + SERVICE_INTERACTIVE_PROCESS: A service which can interact with the desktop. + +Note that a service may only be configured as interactive if it runs under +the LocalSystem account. The safe way to configure an interactive service +is in two stages as follows. + + nssm reset ObjectName + nssm set Type SERVICE_INTERACTIVE_PROCESS + + +Controlling services using the command line +------------------------------------------- +NSSM offers rudimentary service control features. + + nssm start + + nssm restart + + nssm stop + + nssm status + + +Removing services using the GUI +------------------------------- +NSSM can also remove services. Run + + nssm remove + +to remove a service. You will prompted for confirmation before the service +is removed. Try not to remove essential system services... + + +Removing service using the command line +--------------------------------------- +To remove a service without confirmation from the GUI, run + + nssm remove confirm + +Try not to remove essential system services... + + +Logging +------- +NSSM logs to the Windows event log. It registers itself as an event log source +and uses unique event IDs for each type of message it logs. New versions may +add event types but existing event IDs will never be changed. + +Because of the way NSSM registers itself you should be aware that you may not +be able to replace the NSSM binary if you have the event viewer open and that +running multiple instances of NSSM from different locations may be confusing if +they are not all the same version. + + +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. diff --git a/Static/influxDB/nssm-2.24/src/account.cpp b/Static/influxDB/nssm-2.24/src/account.cpp new file mode 100644 index 0000000..70bfa3e --- /dev/null +++ b/Static/influxDB/nssm-2.24/src/account.cpp @@ -0,0 +1,346 @@ +#include "nssm.h" + +#include + +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; +} diff --git a/Static/influxDB/nssm-2.24/src/account.h b/Static/influxDB/nssm-2.24/src/account.h new file mode 100644 index 0000000..e6b40f0 --- /dev/null +++ b/Static/influxDB/nssm-2.24/src/account.h @@ -0,0 +1,24 @@ +#ifndef ACCOUNT_H +#define ACCOUNT_H + +#include + +/* 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 diff --git a/Static/influxDB/nssm-2.24/src/console.cpp b/Static/influxDB/nssm-2.24/src/console.cpp new file mode 100644 index 0000000..66c99ea --- /dev/null +++ b/Static/influxDB/nssm-2.24/src/console.cpp @@ -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++; +} diff --git a/Static/influxDB/nssm-2.24/src/console.h b/Static/influxDB/nssm-2.24/src/console.h new file mode 100644 index 0000000..8aa8966 --- /dev/null +++ b/Static/influxDB/nssm-2.24/src/console.h @@ -0,0 +1,7 @@ +#ifndef CONSOLE_H +#define CONSOLE_H + +void check_console(); +void alloc_console(nssm_service_t *); + +#endif diff --git a/Static/influxDB/nssm-2.24/src/env.cpp b/Static/influxDB/nssm-2.24/src/env.cpp new file mode 100644 index 0000000..063b60f --- /dev/null +++ b/Static/influxDB/nssm-2.24/src/env.cpp @@ -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); +} diff --git a/Static/influxDB/nssm-2.24/src/env.h b/Static/influxDB/nssm-2.24/src/env.h new file mode 100644 index 0000000..021ff4f --- /dev/null +++ b/Static/influxDB/nssm-2.24/src/env.h @@ -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 diff --git a/Static/influxDB/nssm-2.24/src/event.cpp b/Static/influxDB/nssm-2.24/src/event.cpp new file mode 100644 index 0000000..cbe7fe6 --- /dev/null +++ b/Static/influxDB/nssm-2.24/src/event.cpp @@ -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(""); + 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(¶ms, 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(¶ms); + + LocalFree(format); + + return ret; +} diff --git a/Static/influxDB/nssm-2.24/src/event.h b/Static/influxDB/nssm-2.24/src/event.h new file mode 100644 index 0000000..858cc49 --- /dev/null +++ b/Static/influxDB/nssm-2.24/src/event.h @@ -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 diff --git a/Static/influxDB/nssm-2.24/src/gui.cpp b/Static/influxDB/nssm-2.24/src/gui.cpp new file mode 100644 index 0000000..f3d26ca --- /dev/null +++ b/Static/influxDB/nssm-2.24/src/gui.cpp @@ -0,0 +1,1176 @@ +#include "nssm.h" + +static enum { NSSM_TAB_APPLICATION, NSSM_TAB_DETAILS, NSSM_TAB_LOGON, NSSM_TAB_DEPENDENCIES, NSSM_TAB_PROCESS, NSSM_TAB_SHUTDOWN, NSSM_TAB_EXIT, NSSM_TAB_IO, NSSM_TAB_ROTATION, NSSM_TAB_ENVIRONMENT, NSSM_NUM_TABS }; +static HWND tablist[NSSM_NUM_TABS]; +static int selected_tab; + +static HWND dialog(const TCHAR *templ, HWND parent, DLGPROC function, LPARAM l) { + /* The caller will deal with GetLastError()... */ + HRSRC resource = FindResourceEx(0, RT_DIALOG, templ, GetUserDefaultLangID()); + if (! resource) { + if (GetLastError() != ERROR_RESOURCE_LANG_NOT_FOUND) return 0; + resource = FindResourceEx(0, RT_DIALOG, templ, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)); + if (! resource) return 0; + } + + HGLOBAL ret = LoadResource(0, resource); + if (! ret) return 0; + + return CreateDialogIndirectParam(0, (DLGTEMPLATE *) ret, parent, function, l); +} + +static HWND dialog(const TCHAR *templ, HWND parent, DLGPROC function) { + return dialog(templ, parent, function, 0); +} + +int nssm_gui(int resource, nssm_service_t *service) { + /* Create window */ + HWND dlg = dialog(MAKEINTRESOURCE(resource), 0, nssm_dlg, (LPARAM) service); + if (! dlg) { + popup_message(0, MB_OK, NSSM_GUI_CREATEDIALOG_FAILED, error_string(GetLastError())); + return 1; + } + + /* Load the icon. */ + HANDLE icon = LoadImage(GetModuleHandle(0), MAKEINTRESOURCE(IDI_NSSM), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 0); + if (icon) SendMessage(dlg, WM_SETICON, ICON_SMALL, (LPARAM) icon); + icon = LoadImage(GetModuleHandle(0), MAKEINTRESOURCE(IDI_NSSM), IMAGE_ICON, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON), 0); + if (icon) SendMessage(dlg, WM_SETICON, ICON_BIG, (LPARAM) icon); + + /* Remember what the window is for. */ + SetWindowLongPtr(dlg, GWLP_USERDATA, (LONG_PTR) resource); + + /* Display the window */ + centre_window(dlg); + ShowWindow(dlg, SW_SHOW); + + /* Set service name if given */ + if (service->name[0]) { + SetDlgItemText(dlg, IDC_NAME, service->name); + /* No point making user click remove if the name is already entered */ + if (resource == IDD_REMOVE) { + HWND button = GetDlgItem(dlg, IDC_REMOVE); + if (button) { + SendMessage(button, WM_LBUTTONDOWN, 0, 0); + SendMessage(button, WM_LBUTTONUP, 0, 0); + } + } + } + + if (resource == IDD_EDIT) { + /* We'll need the service handle later. */ + SetWindowLongPtr(dlg, DWLP_USER, (LONG_PTR) service); + + /* Service name can't be edited. */ + EnableWindow(GetDlgItem(dlg, IDC_NAME), 0); + SetFocus(GetDlgItem(dlg, IDOK)); + + /* Set existing details. */ + HWND combo; + HWND list; + + /* Application tab. */ + if (service->native) SetDlgItemText(tablist[NSSM_TAB_APPLICATION], IDC_PATH, service->image); + else SetDlgItemText(tablist[NSSM_TAB_APPLICATION], IDC_PATH, service->exe); + SetDlgItemText(tablist[NSSM_TAB_APPLICATION], IDC_DIR, service->dir); + SetDlgItemText(tablist[NSSM_TAB_APPLICATION], IDC_FLAGS, service->flags); + + /* Details tab. */ + SetDlgItemText(tablist[NSSM_TAB_DETAILS], IDC_DISPLAYNAME, service->displayname); + SetDlgItemText(tablist[NSSM_TAB_DETAILS], IDC_DESCRIPTION, service->description); + combo = GetDlgItem(tablist[NSSM_TAB_DETAILS], IDC_STARTUP); + SendMessage(combo, CB_SETCURSEL, service->startup, 0); + + /* Log on tab. */ + if (service->username) { + CheckRadioButton(tablist[NSSM_TAB_LOGON], IDC_LOCALSYSTEM, IDC_ACCOUNT, IDC_ACCOUNT); + SetDlgItemText(tablist[NSSM_TAB_LOGON], IDC_USERNAME, service->username); + EnableWindow(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_INTERACT), 0); + EnableWindow(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_USERNAME), 1); + EnableWindow(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_PASSWORD1), 1); + EnableWindow(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_PASSWORD2), 1); + } + else { + CheckRadioButton(tablist[NSSM_TAB_LOGON], IDC_LOCALSYSTEM, IDC_ACCOUNT, IDC_LOCALSYSTEM); + if (service->type & SERVICE_INTERACTIVE_PROCESS) SendDlgItemMessage(tablist[NSSM_TAB_LOGON], IDC_INTERACT, BM_SETCHECK, BST_CHECKED, 0); + } + + /* Dependencies tab. */ + if (service->dependencieslen) { + TCHAR *formatted; + unsigned long newlen; + if (format_double_null(service->dependencies, service->dependencieslen, &formatted, &newlen)) { + popup_message(dlg, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("dependencies"), _T("nssm_dlg()")); + } + else { + SetDlgItemText(tablist[NSSM_TAB_DEPENDENCIES], IDC_DEPENDENCIES, formatted); + HeapFree(GetProcessHeap(), 0, formatted); + } + } + + /* Process tab. */ + if (service->priority) { + int priority = priority_constant_to_index(service->priority); + combo = GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_PRIORITY); + SendMessage(combo, CB_SETCURSEL, priority, 0); + } + + if (service->affinity) { + list = GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_AFFINITY); + SendDlgItemMessage(tablist[NSSM_TAB_PROCESS], IDC_AFFINITY_ALL, BM_SETCHECK, BST_UNCHECKED, 0); + EnableWindow(GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_AFFINITY), 1); + + DWORD_PTR affinity, system_affinity; + if (GetProcessAffinityMask(GetCurrentProcess(), &affinity, &system_affinity)) { + if ((service->affinity & (__int64) system_affinity) != service->affinity) popup_message(dlg, MB_OK | MB_ICONWARNING, NSSM_GUI_WARN_AFFINITY); + } + + for (int i = 0; i < num_cpus(); i++) { + if (! (service->affinity & (1LL << (__int64) i))) SendMessage(list, LB_SETSEL, 0, i); + } + } + + if (service->no_console) { + SendDlgItemMessage(tablist[NSSM_TAB_PROCESS], IDC_CONSOLE, BM_SETCHECK, BST_UNCHECKED, 0); + } + + /* Shutdown tab. */ + if (! (service->stop_method & NSSM_STOP_METHOD_CONSOLE)) { + SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_CONSOLE, BM_SETCHECK, BST_UNCHECKED, 0); + EnableWindow(GetDlgItem(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_CONSOLE), 0); + } + SetDlgItemInt(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_CONSOLE, service->kill_console_delay, 0); + if (! (service->stop_method & NSSM_STOP_METHOD_WINDOW)) { + SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_WINDOW, BM_SETCHECK, BST_UNCHECKED, 0); + EnableWindow(GetDlgItem(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_WINDOW), 0); + } + SetDlgItemInt(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_WINDOW, service->kill_window_delay, 0); + if (! (service->stop_method & NSSM_STOP_METHOD_THREADS)) { + SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_THREADS, BM_SETCHECK, BST_UNCHECKED, 0); + EnableWindow(GetDlgItem(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_THREADS), 0); + } + SetDlgItemInt(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_THREADS, service->kill_threads_delay, 0); + if (! (service->stop_method & NSSM_STOP_METHOD_TERMINATE)) { + SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_TERMINATE, BM_SETCHECK, BST_UNCHECKED, 0); + } + + /* Restart tab. */ + SetDlgItemInt(tablist[NSSM_TAB_EXIT], IDC_THROTTLE, service->throttle_delay, 0); + combo = GetDlgItem(tablist[NSSM_TAB_EXIT], IDC_APPEXIT); + SendMessage(combo, CB_SETCURSEL, service->default_exit_action, 0); + SetDlgItemInt(tablist[NSSM_TAB_EXIT], IDC_RESTART_DELAY, service->restart_delay, 0); + + /* I/O tab. */ + SetDlgItemText(tablist[NSSM_TAB_IO], IDC_STDIN, service->stdin_path); + SetDlgItemText(tablist[NSSM_TAB_IO], IDC_STDOUT, service->stdout_path); + SetDlgItemText(tablist[NSSM_TAB_IO], IDC_STDERR, service->stderr_path); + + /* Rotation tab. */ + if (service->stdout_disposition == CREATE_ALWAYS) SendDlgItemMessage(tablist[NSSM_TAB_ROTATION], IDC_TRUNCATE, BM_SETCHECK, BST_CHECKED, 0); + if (service->rotate_files) { + SendDlgItemMessage(tablist[NSSM_TAB_ROTATION], IDC_ROTATE, BM_SETCHECK, BST_CHECKED, 0); + EnableWindow(GetDlgItem(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_ONLINE), 1); + EnableWindow(GetDlgItem(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_SECONDS), 1); + EnableWindow(GetDlgItem(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_BYTES_LOW), 1); + } + if (service->rotate_stdout_online || service->rotate_stderr_online) SendDlgItemMessage(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_ONLINE, BM_SETCHECK, BST_CHECKED, 0); + SetDlgItemInt(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_SECONDS, service->rotate_seconds, 0); + if (! service->rotate_bytes_high) SetDlgItemInt(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_BYTES_LOW, service->rotate_bytes_low, 0); + + /* Check if advanced settings are in use. */ + if (service->stdout_disposition != service->stderr_disposition || (service->stdout_disposition && service->stdout_disposition != NSSM_STDOUT_DISPOSITION && service->stdout_disposition != CREATE_ALWAYS) || (service->stderr_disposition && service->stderr_disposition != NSSM_STDERR_DISPOSITION && service->stderr_disposition != CREATE_ALWAYS)) popup_message(dlg, MB_OK | MB_ICONWARNING, NSSM_GUI_WARN_STDIO); + if (service->rotate_bytes_high) popup_message(dlg, MB_OK | MB_ICONWARNING, NSSM_GUI_WARN_ROTATE_BYTES); + + /* Environment tab. */ + TCHAR *env; + unsigned long envlen; + if (service->env_extralen) { + env = service->env_extra; + envlen = service->env_extralen; + } + else { + env = service->env; + envlen = service->envlen; + if (envlen) SendDlgItemMessage(tablist[NSSM_TAB_ENVIRONMENT], IDC_ENVIRONMENT_REPLACE, BM_SETCHECK, BST_CHECKED, 0); + } + + if (envlen) { + TCHAR *formatted; + unsigned long newlen; + if (format_double_null(env, envlen, &formatted, &newlen)) { + popup_message(dlg, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("environment"), _T("nssm_dlg()")); + } + else { + SetDlgItemText(tablist[NSSM_TAB_ENVIRONMENT], IDC_ENVIRONMENT, formatted); + HeapFree(GetProcessHeap(), 0, formatted); + } + } + if (service->envlen && service->env_extralen) popup_message(dlg, MB_OK | MB_ICONWARNING, NSSM_GUI_WARN_ENVIRONMENT); + } + + /* Go! */ + MSG message; + while (GetMessage(&message, 0, 0, 0)) { + if (IsDialogMessage(dlg, &message)) continue; + TranslateMessage(&message); + DispatchMessage(&message); + } + + return (int) message.wParam; +} + +void centre_window(HWND window) { + HWND desktop; + RECT size, desktop_size; + unsigned long x, y; + + if (! window) return; + + /* Find window size */ + if (! GetWindowRect(window, &size)) return; + + /* Find desktop window */ + desktop = GetDesktopWindow(); + if (! desktop) return; + + /* Find desktop window size */ + if (! GetWindowRect(desktop, &desktop_size)) return; + + /* Centre window */ + x = (desktop_size.right - size.right) / 2; + y = (desktop_size.bottom - size.bottom) / 2; + MoveWindow(window, x, y, size.right - size.left, size.bottom - size.top, 0); +} + +static inline void check_stop_method(nssm_service_t *service, unsigned long method, unsigned long control) { + if (SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], control, BM_GETCHECK, 0, 0) & BST_CHECKED) return; + service->stop_method &= ~method; +} + +static inline void check_number(HWND tab, unsigned long control, unsigned long *timeout) { + BOOL translated; + unsigned long configured = GetDlgItemInt(tab, control, &translated, 0); + if (translated) *timeout = configured; +} + +static inline void set_timeout_enabled(unsigned long control, unsigned long dependent) { + unsigned char enabled = 0; + if (SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], control, BM_GETCHECK, 0, 0) & BST_CHECKED) enabled = 1; + EnableWindow(GetDlgItem(tablist[NSSM_TAB_SHUTDOWN], dependent), enabled); +} + +static inline void set_logon_enabled(unsigned char enabled) { + EnableWindow(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_INTERACT), ! enabled); + EnableWindow(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_USERNAME), enabled); + EnableWindow(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_PASSWORD1), enabled); + EnableWindow(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_PASSWORD2), enabled); +} + +static inline void set_affinity_enabled(unsigned char enabled) { + EnableWindow(GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_AFFINITY), enabled); +} + +static inline void set_rotation_enabled(unsigned char enabled) { + EnableWindow(GetDlgItem(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_ONLINE), enabled); + EnableWindow(GetDlgItem(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_SECONDS), enabled); + EnableWindow(GetDlgItem(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_BYTES_LOW), enabled); +} + +static inline void check_io(HWND owner, TCHAR *name, TCHAR *buffer, unsigned long len, unsigned long control) { + if (! SendMessage(GetDlgItem(tablist[NSSM_TAB_IO], control), WM_GETTEXTLENGTH, 0, 0)) return; + if (GetDlgItemText(tablist[NSSM_TAB_IO], control, buffer, (int) len)) return; + popup_message(owner, MB_OK | MB_ICONEXCLAMATION, NSSM_MESSAGE_PATH_TOO_LONG, name); + ZeroMemory(buffer, len * sizeof(TCHAR)); +} + +/* Set service parameters. */ +int configure(HWND window, nssm_service_t *service, nssm_service_t *orig_service) { + if (! service) return 1; + + set_nssm_service_defaults(service); + + if (orig_service) { + service->native = orig_service->native; + service->handle = orig_service->handle; + } + + /* Get service name. */ + if (! GetDlgItemText(window, IDC_NAME, service->name, _countof(service->name))) { + popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_SERVICE_NAME); + cleanup_nssm_service(service); + return 2; + } + + /* Get executable name */ + if (! service->native) { + if (! GetDlgItemText(tablist[NSSM_TAB_APPLICATION], IDC_PATH, service->exe, _countof(service->exe))) { + popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_PATH); + return 3; + } + + /* Get startup directory. */ + if (! GetDlgItemText(tablist[NSSM_TAB_APPLICATION], IDC_DIR, service->dir, _countof(service->dir))) { + _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe); + strip_basename(service->dir); + } + + /* Get flags. */ + if (SendMessage(GetDlgItem(tablist[NSSM_TAB_APPLICATION], IDC_FLAGS), WM_GETTEXTLENGTH, 0, 0)) { + if (! GetDlgItemText(tablist[NSSM_TAB_APPLICATION], IDC_FLAGS, service->flags, _countof(service->flags))) { + popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_OPTIONS); + return 4; + } + } + } + + /* Get details. */ + if (SendMessage(GetDlgItem(tablist[NSSM_TAB_DETAILS], IDC_DISPLAYNAME), WM_GETTEXTLENGTH, 0, 0)) { + if (! GetDlgItemText(tablist[NSSM_TAB_DETAILS], IDC_DISPLAYNAME, service->displayname, _countof(service->displayname))) { + popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_DISPLAYNAME); + return 5; + } + } + + if (SendMessage(GetDlgItem(tablist[NSSM_TAB_DETAILS], IDC_DESCRIPTION), WM_GETTEXTLENGTH, 0, 0)) { + if (! GetDlgItemText(tablist[NSSM_TAB_DETAILS], IDC_DESCRIPTION, service->description, _countof(service->description))) { + popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_DESCRIPTION); + return 5; + } + } + + HWND combo = GetDlgItem(tablist[NSSM_TAB_DETAILS], IDC_STARTUP); + service->startup = (unsigned long) SendMessage(combo, CB_GETCURSEL, 0, 0); + if (service->startup == CB_ERR) service->startup = 0; + + /* Get logon stuff. */ + if (SendDlgItemMessage(tablist[NSSM_TAB_LOGON], IDC_LOCALSYSTEM, BM_GETCHECK, 0, 0) & BST_CHECKED) { + if (SendDlgItemMessage(tablist[NSSM_TAB_LOGON], IDC_INTERACT, BM_GETCHECK, 0, 0) & BST_CHECKED) { + service->type |= SERVICE_INTERACTIVE_PROCESS; + } + if (service->username) HeapFree(GetProcessHeap(), 0, service->username); + service->username = 0; + service->usernamelen = 0; + if (service->password) { + SecureZeroMemory(service->password, service->passwordlen); + HeapFree(GetProcessHeap(), 0, service->password); + } + service->password = 0; + service->passwordlen = 0; + } + else { + /* Username. */ + service->usernamelen = SendMessage(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_USERNAME), WM_GETTEXTLENGTH, 0, 0); + if (! service->usernamelen) { + popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_USERNAME); + return 6; + } + service->usernamelen++; + + service->username = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, service->usernamelen * sizeof(TCHAR)); + if (! service->username) { + popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("account name"), _T("install()")); + return 6; + } + if (! GetDlgItemText(tablist[NSSM_TAB_LOGON], IDC_USERNAME, service->username, (int) service->usernamelen)) { + HeapFree(GetProcessHeap(), 0, service->username); + service->username = 0; + service->usernamelen = 0; + popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_USERNAME); + return 6; + } + + /* + Special case for well-known accounts. + Ignore the password if we're editing and the username hasn't changed. + */ + const TCHAR *well_known = well_known_username(service->username); + if (well_known) { + if (str_equiv(well_known, NSSM_LOCALSYSTEM_ACCOUNT)) { + HeapFree(GetProcessHeap(), 0, service->username); + service->username = 0; + service->usernamelen = 0; + } + else { + service->usernamelen = _tcslen(well_known) + 1; + service->username = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, service->usernamelen * sizeof(TCHAR)); + if (! service->username) { + print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canon"), _T("install()")); + return 6; + } + memmove(service->username, well_known, service->usernamelen * sizeof(TCHAR)); + } + } + else { + /* Password. */ + service->passwordlen = SendMessage(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_PASSWORD1), WM_GETTEXTLENGTH, 0, 0); + size_t passwordlen = SendMessage(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_PASSWORD2), WM_GETTEXTLENGTH, 0, 0); + + if (! orig_service || ! orig_service->username || ! str_equiv(service->username, orig_service->username) || service->passwordlen || passwordlen) { + if (! service->passwordlen) { + popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_PASSWORD); + return 6; + } + if (passwordlen != service->passwordlen) { + popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_PASSWORD); + return 6; + } + service->passwordlen++; + + /* Temporary buffer for password validation. */ + TCHAR *password = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, service->passwordlen * sizeof(TCHAR)); + if (! password) { + HeapFree(GetProcessHeap(), 0, service->username); + service->username = 0; + service->usernamelen = 0; + popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("password confirmation"), _T("install()")); + return 6; + } + + /* Actual password buffer. */ + service->password = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, service->passwordlen * sizeof(TCHAR)); + if (! service->password) { + HeapFree(GetProcessHeap(), 0, password); + HeapFree(GetProcessHeap(), 0, service->username); + service->username = 0; + service->usernamelen = 0; + popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("password"), _T("install()")); + return 6; + } + + /* Get first password. */ + if (! GetDlgItemText(tablist[NSSM_TAB_LOGON], IDC_PASSWORD1, service->password, (int) service->passwordlen)) { + HeapFree(GetProcessHeap(), 0, password); + SecureZeroMemory(service->password, service->passwordlen); + HeapFree(GetProcessHeap(), 0, service->password); + service->password = 0; + service->passwordlen = 0; + HeapFree(GetProcessHeap(), 0, service->username); + service->username = 0; + service->usernamelen = 0; + popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_PASSWORD); + return 6; + } + + /* Get confirmation. */ + if (! GetDlgItemText(tablist[NSSM_TAB_LOGON], IDC_PASSWORD2, password, (int) service->passwordlen)) { + SecureZeroMemory(password, service->passwordlen); + HeapFree(GetProcessHeap(), 0, password); + SecureZeroMemory(service->password, service->passwordlen); + HeapFree(GetProcessHeap(), 0, service->password); + service->password = 0; + service->passwordlen = 0; + HeapFree(GetProcessHeap(), 0, service->username); + service->username = 0; + service->usernamelen = 0; + popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_PASSWORD); + return 6; + } + + /* Compare. */ + if (_tcsncmp(password, service->password, service->passwordlen)) { + popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_PASSWORD); + SecureZeroMemory(password, service->passwordlen); + HeapFree(GetProcessHeap(), 0, password); + SecureZeroMemory(service->password, service->passwordlen); + HeapFree(GetProcessHeap(), 0, service->password); + service->password = 0; + service->passwordlen = 0; + HeapFree(GetProcessHeap(), 0, service->username); + service->username = 0; + service->usernamelen = 0; + return 6; + } + } + } + } + + /* Get dependencies. */ + unsigned long dependencieslen = (unsigned long) SendMessage(GetDlgItem(tablist[NSSM_TAB_DEPENDENCIES], IDC_DEPENDENCIES), WM_GETTEXTLENGTH, 0, 0); + if (dependencieslen) { + TCHAR *dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (dependencieslen + 2) * sizeof(TCHAR)); + if (! dependencies) { + popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("dependencies"), _T("install()")); + cleanup_nssm_service(service); + return 6; + } + + if (! GetDlgItemText(tablist[NSSM_TAB_DEPENDENCIES], IDC_DEPENDENCIES, dependencies, dependencieslen + 1)) { + popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_DEPENDENCIES); + HeapFree(GetProcessHeap(), 0, dependencies); + cleanup_nssm_service(service); + return 6; + } + + if (unformat_double_null(dependencies, dependencieslen, &service->dependencies, &service->dependencieslen)) { + HeapFree(GetProcessHeap(), 0, dependencies); + popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("dependencies"), _T("install()")); + cleanup_nssm_service(service); + return 6; + } + + HeapFree(GetProcessHeap(), 0, dependencies); + } + + /* Remaining tabs are only for services we manage. */ + if (service->native) return 0; + + /* Get process stuff. */ + combo = GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_PRIORITY); + service->priority = priority_index_to_constant((unsigned long) SendMessage(combo, CB_GETCURSEL, 0, 0)); + + service->affinity = 0LL; + if (! (SendDlgItemMessage(tablist[NSSM_TAB_PROCESS], IDC_AFFINITY_ALL, BM_GETCHECK, 0, 0) & BST_CHECKED)) { + HWND list = GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_AFFINITY); + int selected = (int) SendMessage(list, LB_GETSELCOUNT, 0, 0); + int count = (int) SendMessage(list, LB_GETCOUNT, 0, 0); + if (! selected) { + popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_WARN_AFFINITY_NONE); + return 5; + } + else if (selected < count) { + for (int i = 0; i < count; i++) { + if (SendMessage(list, LB_GETSEL, i, 0)) service->affinity |= (1LL << (__int64) i); + } + } + } + + if (SendDlgItemMessage(tablist[NSSM_TAB_PROCESS], IDC_CONSOLE, BM_GETCHECK, 0, 0) & BST_CHECKED) service->no_console = 0; + else service->no_console = 1; + + /* Get stop method stuff. */ + check_stop_method(service, NSSM_STOP_METHOD_CONSOLE, IDC_METHOD_CONSOLE); + check_stop_method(service, NSSM_STOP_METHOD_WINDOW, IDC_METHOD_WINDOW); + check_stop_method(service, NSSM_STOP_METHOD_THREADS, IDC_METHOD_THREADS); + check_stop_method(service, NSSM_STOP_METHOD_TERMINATE, IDC_METHOD_TERMINATE); + check_number(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_CONSOLE, &service->kill_console_delay); + check_number(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_WINDOW, &service->kill_window_delay); + check_number(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_THREADS, &service->kill_threads_delay); + + /* Get exit action stuff. */ + check_number(tablist[NSSM_TAB_EXIT], IDC_THROTTLE, &service->throttle_delay); + combo = GetDlgItem(tablist[NSSM_TAB_EXIT], IDC_APPEXIT); + service->default_exit_action = (unsigned long) SendMessage(combo, CB_GETCURSEL, 0, 0); + if (service->default_exit_action == CB_ERR) service->default_exit_action = 0; + check_number(tablist[NSSM_TAB_EXIT], IDC_RESTART_DELAY, &service->restart_delay); + + /* Get I/O stuff. */ + check_io(window, _T("stdin"), service->stdin_path, _countof(service->stdin_path), IDC_STDIN); + check_io(window, _T("stdout"), service->stdout_path, _countof(service->stdout_path), IDC_STDOUT); + check_io(window, _T("stderr"), service->stderr_path, _countof(service->stderr_path), IDC_STDERR); + + /* Override stdout and/or stderr. */ + if (SendDlgItemMessage(tablist[NSSM_TAB_ROTATION], IDC_TRUNCATE, BM_GETCHECK, 0, 0) & BST_CHECKED) { + if (service->stdout_path[0]) service->stdout_disposition = CREATE_ALWAYS; + if (service->stderr_path[0]) service->stderr_disposition = CREATE_ALWAYS; + } + + /* Get rotation stuff. */ + if (SendDlgItemMessage(tablist[NSSM_TAB_ROTATION], IDC_ROTATE, BM_GETCHECK, 0, 0) & BST_CHECKED) { + service->rotate_files = true; + if (SendDlgItemMessage(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_ONLINE, BM_GETCHECK, 0, 0) & BST_CHECKED) service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_ONLINE; + check_number(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_SECONDS, &service->rotate_seconds); + check_number(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_BYTES_LOW, &service->rotate_bytes_low); + } + + /* Get environment. */ + unsigned long envlen = (unsigned long) SendMessage(GetDlgItem(tablist[NSSM_TAB_ENVIRONMENT], IDC_ENVIRONMENT), WM_GETTEXTLENGTH, 0, 0); + if (envlen) { + TCHAR *env = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (envlen + 2) * sizeof(TCHAR)); + if (! env) { + popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("environment"), _T("install()")); + cleanup_nssm_service(service); + return 5; + } + + if (! GetDlgItemText(tablist[NSSM_TAB_ENVIRONMENT], IDC_ENVIRONMENT, env, envlen + 1)) { + popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_ENVIRONMENT); + HeapFree(GetProcessHeap(), 0, env); + cleanup_nssm_service(service); + return 5; + } + + TCHAR *newenv; + unsigned long newlen; + if (unformat_double_null(env, envlen, &newenv, &newlen)) { + HeapFree(GetProcessHeap(), 0, env); + popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("environment"), _T("install()")); + cleanup_nssm_service(service); + return 5; + } + + HeapFree(GetProcessHeap(), 0, env); + env = newenv; + envlen = newlen; + + /* Test the environment is valid. */ + if (test_environment(env)) { + popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_ENVIRONMENT); + HeapFree(GetProcessHeap(), 0, env); + cleanup_nssm_service(service); + return 5; + } + + if (SendDlgItemMessage(tablist[NSSM_TAB_ENVIRONMENT], IDC_ENVIRONMENT_REPLACE, BM_GETCHECK, 0, 0) & BST_CHECKED) { + service->env = env; + service->envlen = envlen; + } + else { + service->env_extra = env; + service->env_extralen = envlen; + } + } + + return 0; +} + +/* Install the service. */ +int install(HWND window) { + if (! window) return 1; + + nssm_service_t *service = alloc_nssm_service(); + if (service) { + int ret = configure(window, service, 0); + if (ret) return ret; + } + + /* See if it works. */ + switch (install_service(service)) { + case 1: + popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("service"), _T("install()")); + cleanup_nssm_service(service); + return 1; + + case 2: + popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED); + cleanup_nssm_service(service); + return 2; + + case 3: + popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_MESSAGE_PATH_TOO_LONG, NSSM); + cleanup_nssm_service(service); + return 3; + + case 4: + popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_OUT_OF_MEMORY_FOR_IMAGEPATH); + cleanup_nssm_service(service); + return 4; + + case 5: + popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INSTALL_SERVICE_FAILED); + cleanup_nssm_service(service); + return 5; + + case 6: + popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_CREATE_PARAMETERS_FAILED); + cleanup_nssm_service(service); + return 6; + } + + popup_message(window, MB_OK, NSSM_MESSAGE_SERVICE_INSTALLED, service->name); + cleanup_nssm_service(service); + return 0; +} + +/* Remove the service */ +int remove(HWND window) { + if (! window) return 1; + + /* See if it works */ + nssm_service_t *service = alloc_nssm_service(); + if (service) { + /* Get service name */ + if (! GetDlgItemText(window, IDC_NAME, service->name, _countof(service->name))) { + popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_SERVICE_NAME); + cleanup_nssm_service(service); + return 2; + } + + /* Confirm */ + if (popup_message(window, MB_YESNO, NSSM_GUI_ASK_REMOVE_SERVICE, service->name) != IDYES) { + cleanup_nssm_service(service); + return 0; + } + } + + switch (remove_service(service)) { + case 1: + popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("service"), _T("remove()")); + cleanup_nssm_service(service); + return 1; + + case 2: + popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED); + cleanup_nssm_service(service); + return 2; + + case 3: + popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_SERVICE_NOT_INSTALLED); + return 3; + cleanup_nssm_service(service); + + case 4: + popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_REMOVE_SERVICE_FAILED); + cleanup_nssm_service(service); + return 4; + } + + popup_message(window, MB_OK, NSSM_MESSAGE_SERVICE_REMOVED, service->name); + cleanup_nssm_service(service); + return 0; +} + +int edit(HWND window, nssm_service_t *orig_service) { + if (! window) return 1; + + nssm_service_t *service = alloc_nssm_service(); + if (service) { + int ret = configure(window, service, orig_service); + if (ret) return ret; + } + + switch (edit_service(service, true)) { + case 1: + popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("service"), _T("edit()")); + cleanup_nssm_service(service); + return 1; + + case 3: + popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_MESSAGE_PATH_TOO_LONG, NSSM); + cleanup_nssm_service(service); + return 3; + + case 4: + popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_OUT_OF_MEMORY_FOR_IMAGEPATH); + cleanup_nssm_service(service); + return 4; + + case 5: + case 6: + popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_EDIT_PARAMETERS_FAILED); + cleanup_nssm_service(service); + return 6; + } + + popup_message(window, MB_OK, NSSM_MESSAGE_SERVICE_EDITED, service->name); + cleanup_nssm_service(service); + return 0; +} + +static TCHAR *browse_filter(int message) { + switch (message) { + case NSSM_GUI_BROWSE_FILTER_APPLICATIONS: return _T("*.exe;*.bat;*.cmd"); + case NSSM_GUI_BROWSE_FILTER_DIRECTORIES: return _T("."); + case NSSM_GUI_BROWSE_FILTER_ALL_FILES: /* Fall through. */ + default: return _T("*.*"); + } +} + +UINT_PTR CALLBACK browse_hook(HWND dlg, UINT message, WPARAM w, LPARAM l) { + switch (message) { + case WM_INITDIALOG: + return 1; + } + + return 0; +} + +/* Browse for application */ +void browse(HWND window, TCHAR *current, unsigned long flags, ...) { + if (! window) return; + + va_list arg; + size_t bufsize = 256; + size_t len = bufsize; + int i; + + OPENFILENAME ofn; + ZeroMemory(&ofn, sizeof(ofn)); + ofn.lStructSize = sizeof(ofn); + ofn.lpstrFilter = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, bufsize * sizeof(TCHAR)); + /* XXX: Escaping nulls with FormatMessage is tricky */ + if (ofn.lpstrFilter) { + ZeroMemory((void *) ofn.lpstrFilter, bufsize); + len = 0; + /* "Applications" + NULL + "*.exe" + NULL */ + va_start(arg, flags); + while (i = va_arg(arg, int)) { + TCHAR *localised = message_string(i); + _sntprintf_s((TCHAR *) ofn.lpstrFilter + len, bufsize, _TRUNCATE, localised); + len += _tcslen(localised) + 1; + LocalFree(localised); + TCHAR *filter = browse_filter(i); + _sntprintf_s((TCHAR *) ofn.lpstrFilter + len, bufsize - len, _TRUNCATE, _T("%s"), filter); + len += _tcslen(filter) + 1; + } + va_end(arg); + /* Remainder of the buffer is already zeroed */ + } + ofn.lpstrFile = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, PATH_LENGTH * sizeof(TCHAR)); + if (ofn.lpstrFile) { + if (flags & OFN_NOVALIDATE) { + /* Directory hack. */ + _sntprintf_s(ofn.lpstrFile, PATH_LENGTH, _TRUNCATE, _T(":%s:"), message_string(NSSM_GUI_BROWSE_FILTER_DIRECTORIES)); + ofn.nMaxFile = DIR_LENGTH; + } + else { + _sntprintf_s(ofn.lpstrFile, PATH_LENGTH, _TRUNCATE, _T("%s"), current); + ofn.nMaxFile = PATH_LENGTH; + } + } + ofn.lpstrTitle = message_string(NSSM_GUI_BROWSE_TITLE); + ofn.Flags = OFN_EXPLORER | OFN_HIDEREADONLY | OFN_PATHMUSTEXIST | flags; + + if (GetOpenFileName(&ofn)) { + /* Directory hack. */ + if (flags & OFN_NOVALIDATE) strip_basename(ofn.lpstrFile); + SendMessage(window, WM_SETTEXT, 0, (LPARAM) ofn.lpstrFile); + } + if (ofn.lpstrFilter) HeapFree(GetProcessHeap(), 0, (void *) ofn.lpstrFilter); + if (ofn.lpstrFile) HeapFree(GetProcessHeap(), 0, ofn.lpstrFile); +} + +INT_PTR CALLBACK tab_dlg(HWND tab, UINT message, WPARAM w, LPARAM l) { + switch (message) { + case WM_INITDIALOG: + return 1; + + /* Button was pressed or control was controlled. */ + case WM_COMMAND: + HWND dlg; + TCHAR buffer[PATH_LENGTH]; + unsigned char enabled; + + switch (LOWORD(w)) { + /* Browse for application. */ + case IDC_BROWSE: + dlg = GetDlgItem(tab, IDC_PATH); + GetDlgItemText(tab, IDC_PATH, buffer, _countof(buffer)); + browse(dlg, buffer, OFN_FILEMUSTEXIST, NSSM_GUI_BROWSE_FILTER_APPLICATIONS, NSSM_GUI_BROWSE_FILTER_ALL_FILES, 0); + /* Fill in startup directory if it wasn't already specified. */ + GetDlgItemText(tab, IDC_DIR, buffer, _countof(buffer)); + if (! buffer[0]) { + GetDlgItemText(tab, IDC_PATH, buffer, _countof(buffer)); + strip_basename(buffer); + SetDlgItemText(tab, IDC_DIR, buffer); + } + break; + + /* Browse for startup directory. */ + case IDC_BROWSE_DIR: + dlg = GetDlgItem(tab, IDC_DIR); + GetDlgItemText(tab, IDC_DIR, buffer, _countof(buffer)); + browse(dlg, buffer, OFN_NOVALIDATE, NSSM_GUI_BROWSE_FILTER_DIRECTORIES, 0); + break; + + /* Log on. */ + case IDC_LOCALSYSTEM: + set_logon_enabled(0); + break; + + case IDC_ACCOUNT: + set_logon_enabled(1); + break; + + /* Affinity. */ + case IDC_AFFINITY_ALL: + if (SendDlgItemMessage(tab, LOWORD(w), BM_GETCHECK, 0, 0) & BST_CHECKED) enabled = 0; + else enabled = 1; + set_affinity_enabled(enabled); + break; + + /* Shutdown methods. */ + case IDC_METHOD_CONSOLE: + set_timeout_enabled(LOWORD(w), IDC_KILL_CONSOLE); + break; + + case IDC_METHOD_WINDOW: + set_timeout_enabled(LOWORD(w), IDC_KILL_WINDOW); + break; + + case IDC_METHOD_THREADS: + set_timeout_enabled(LOWORD(w), IDC_KILL_THREADS); + break; + + /* Browse for stdin. */ + case IDC_BROWSE_STDIN: + dlg = GetDlgItem(tab, IDC_STDIN); + GetDlgItemText(tab, IDC_STDIN, buffer, _countof(buffer)); + browse(dlg, buffer, 0, NSSM_GUI_BROWSE_FILTER_ALL_FILES, 0); + break; + + /* Browse for stdout. */ + case IDC_BROWSE_STDOUT: + dlg = GetDlgItem(tab, IDC_STDOUT); + GetDlgItemText(tab, IDC_STDOUT, buffer, _countof(buffer)); + browse(dlg, buffer, 0, NSSM_GUI_BROWSE_FILTER_ALL_FILES, 0); + /* Fill in stderr if it wasn't already specified. */ + GetDlgItemText(tab, IDC_STDERR, buffer, _countof(buffer)); + if (! buffer[0]) { + GetDlgItemText(tab, IDC_STDOUT, buffer, _countof(buffer)); + SetDlgItemText(tab, IDC_STDERR, buffer); + } + break; + + /* Browse for stderr. */ + case IDC_BROWSE_STDERR: + dlg = GetDlgItem(tab, IDC_STDERR); + GetDlgItemText(tab, IDC_STDERR, buffer, _countof(buffer)); + browse(dlg, buffer, 0, NSSM_GUI_BROWSE_FILTER_ALL_FILES, 0); + break; + + /* Rotation. */ + case IDC_ROTATE: + if (SendDlgItemMessage(tab, LOWORD(w), BM_GETCHECK, 0, 0) & BST_CHECKED) enabled = 1; + else enabled = 0; + set_rotation_enabled(enabled); + break; + } + return 1; + } + + return 0; +} + +/* Install/remove dialogue callback */ +INT_PTR CALLBACK nssm_dlg(HWND window, UINT message, WPARAM w, LPARAM l) { + nssm_service_t *service; + + switch (message) { + /* Creating the dialogue */ + case WM_INITDIALOG: + service = (nssm_service_t *) l; + + SetFocus(GetDlgItem(window, IDC_NAME)); + + HWND tabs; + HWND combo; + HWND list; + int i, n; + tabs = GetDlgItem(window, IDC_TAB1); + if (! tabs) return 0; + + /* Set up tabs. */ + TCITEM tab; + ZeroMemory(&tab, sizeof(tab)); + tab.mask = TCIF_TEXT; + + selected_tab = 0; + + /* Application tab. */ + if (service->native) tab.pszText = message_string(NSSM_GUI_TAB_NATIVE); + else tab.pszText = message_string(NSSM_GUI_TAB_APPLICATION); + tab.cchTextMax = (int) _tcslen(tab.pszText); + SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_APPLICATION, (LPARAM) &tab); + if (service->native) { + tablist[NSSM_TAB_APPLICATION] = dialog(MAKEINTRESOURCE(IDD_NATIVE), window, tab_dlg); + EnableWindow(tablist[NSSM_TAB_APPLICATION], 0); + EnableWindow(GetDlgItem(tablist[NSSM_TAB_APPLICATION], IDC_PATH), 0); + } + else tablist[NSSM_TAB_APPLICATION] = dialog(MAKEINTRESOURCE(IDD_APPLICATION), window, tab_dlg); + ShowWindow(tablist[NSSM_TAB_APPLICATION], SW_SHOW); + + /* Details tab. */ + tab.pszText = message_string(NSSM_GUI_TAB_DETAILS); + tab.cchTextMax = (int) _tcslen(tab.pszText); + SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_DETAILS, (LPARAM) &tab); + tablist[NSSM_TAB_DETAILS] = dialog(MAKEINTRESOURCE(IDD_DETAILS), window, tab_dlg); + ShowWindow(tablist[NSSM_TAB_DETAILS], SW_HIDE); + + /* Set defaults. */ + combo = GetDlgItem(tablist[NSSM_TAB_DETAILS], IDC_STARTUP); + SendMessage(combo, CB_INSERTSTRING, NSSM_STARTUP_AUTOMATIC, (LPARAM) message_string(NSSM_GUI_STARTUP_AUTOMATIC)); + SendMessage(combo, CB_INSERTSTRING, NSSM_STARTUP_DELAYED, (LPARAM) message_string(NSSM_GUI_STARTUP_DELAYED)); + SendMessage(combo, CB_INSERTSTRING, NSSM_STARTUP_MANUAL, (LPARAM) message_string(NSSM_GUI_STARTUP_MANUAL)); + SendMessage(combo, CB_INSERTSTRING, NSSM_STARTUP_DISABLED, (LPARAM) message_string(NSSM_GUI_STARTUP_DISABLED)); + SendMessage(combo, CB_SETCURSEL, NSSM_STARTUP_AUTOMATIC, 0); + + /* Logon tab. */ + tab.pszText = message_string(NSSM_GUI_TAB_LOGON); + tab.cchTextMax = (int) _tcslen(tab.pszText); + SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_LOGON, (LPARAM) &tab); + tablist[NSSM_TAB_LOGON] = dialog(MAKEINTRESOURCE(IDD_LOGON), window, tab_dlg); + ShowWindow(tablist[NSSM_TAB_LOGON], SW_HIDE); + + /* Set defaults. */ + CheckRadioButton(tablist[NSSM_TAB_LOGON], IDC_LOCALSYSTEM, IDC_ACCOUNT, IDC_LOCALSYSTEM); + set_logon_enabled(0); + + /* Dependencies tab. */ + tab.pszText = message_string(NSSM_GUI_TAB_DEPENDENCIES); + tab.cchTextMax = (int) _tcslen(tab.pszText); + SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_DEPENDENCIES, (LPARAM) &tab); + tablist[NSSM_TAB_DEPENDENCIES] = dialog(MAKEINTRESOURCE(IDD_DEPENDENCIES), window, tab_dlg); + ShowWindow(tablist[NSSM_TAB_DEPENDENCIES], SW_HIDE); + + /* Remaining tabs are only for services we manage. */ + if (service->native) return 1; + + /* Process tab. */ + tab.pszText = message_string(NSSM_GUI_TAB_PROCESS); + tab.cchTextMax = (int) _tcslen(tab.pszText); + SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_PROCESS, (LPARAM) &tab); + tablist[NSSM_TAB_PROCESS] = dialog(MAKEINTRESOURCE(IDD_PROCESS), window, tab_dlg); + ShowWindow(tablist[NSSM_TAB_PROCESS], SW_HIDE); + + /* Set defaults. */ + combo = GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_PRIORITY); + SendMessage(combo, CB_INSERTSTRING, NSSM_REALTIME_PRIORITY, (LPARAM) message_string(NSSM_GUI_REALTIME_PRIORITY_CLASS)); + SendMessage(combo, CB_INSERTSTRING, NSSM_HIGH_PRIORITY, (LPARAM) message_string(NSSM_GUI_HIGH_PRIORITY_CLASS)); + SendMessage(combo, CB_INSERTSTRING, NSSM_ABOVE_NORMAL_PRIORITY, (LPARAM) message_string(NSSM_GUI_ABOVE_NORMAL_PRIORITY_CLASS)); + SendMessage(combo, CB_INSERTSTRING, NSSM_NORMAL_PRIORITY, (LPARAM) message_string(NSSM_GUI_NORMAL_PRIORITY_CLASS)); + SendMessage(combo, CB_INSERTSTRING, NSSM_BELOW_NORMAL_PRIORITY, (LPARAM) message_string(NSSM_GUI_BELOW_NORMAL_PRIORITY_CLASS)); + SendMessage(combo, CB_INSERTSTRING, NSSM_IDLE_PRIORITY, (LPARAM) message_string(NSSM_GUI_IDLE_PRIORITY_CLASS)); + SendMessage(combo, CB_SETCURSEL, NSSM_NORMAL_PRIORITY, 0); + + SendDlgItemMessage(tablist[NSSM_TAB_PROCESS], IDC_CONSOLE, BM_SETCHECK, BST_CHECKED, 0); + + list = GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_AFFINITY); + n = num_cpus(); + SendMessage(list, LB_SETCOLUMNWIDTH, 16, 0); + for (i = 0; i < n; i++) { + TCHAR buffer[3]; + _sntprintf_s(buffer, _countof(buffer), _TRUNCATE, _T("%d"), i); + SendMessage(list, LB_ADDSTRING, 0, (LPARAM) buffer); + } + + /* + Size to fit. + The box is high enough for four rows. It is wide enough for eight + columns without scrolling. With scrollbars it shrinks to two rows. + Note that the above only holds if we set the column width BEFORE + adding the strings. + */ + if (n < 32) { + int columns = (n - 1) / 4; + RECT rect; + GetWindowRect(list, &rect); + int width = rect.right - rect.left; + width -= (7 - columns) * 16; + int height = rect.bottom - rect.top; + if (n < 4) height -= (int) SendMessage(list, LB_GETITEMHEIGHT, 0, 0) * (4 - n); + SetWindowPos(list, 0, 0, 0, width, height, SWP_NOMOVE | SWP_NOOWNERZORDER); + } + SendMessage(list, LB_SELITEMRANGE, 1, MAKELPARAM(0, n)); + + SendDlgItemMessage(tablist[NSSM_TAB_PROCESS], IDC_AFFINITY_ALL, BM_SETCHECK, BST_CHECKED, 0); + set_affinity_enabled(0); + + /* Shutdown tab. */ + tab.pszText = message_string(NSSM_GUI_TAB_SHUTDOWN); + tab.cchTextMax = (int) _tcslen(tab.pszText); + SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_SHUTDOWN, (LPARAM) &tab); + tablist[NSSM_TAB_SHUTDOWN] = dialog(MAKEINTRESOURCE(IDD_SHUTDOWN), window, tab_dlg); + ShowWindow(tablist[NSSM_TAB_SHUTDOWN], SW_HIDE); + + /* Set defaults. */ + SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_CONSOLE, BM_SETCHECK, BST_CHECKED, 0); + SetDlgItemInt(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_CONSOLE, NSSM_KILL_CONSOLE_GRACE_PERIOD, 0); + SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_WINDOW, BM_SETCHECK, BST_CHECKED, 0); + SetDlgItemInt(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_WINDOW, NSSM_KILL_WINDOW_GRACE_PERIOD, 0); + SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_THREADS, BM_SETCHECK, BST_CHECKED, 0); + SetDlgItemInt(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_THREADS, NSSM_KILL_THREADS_GRACE_PERIOD, 0); + SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_TERMINATE, BM_SETCHECK, BST_CHECKED, 0); + + /* Restart tab. */ + tab.pszText = message_string(NSSM_GUI_TAB_EXIT); + tab.cchTextMax = (int) _tcslen(tab.pszText); + SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_EXIT, (LPARAM) &tab); + tablist[NSSM_TAB_EXIT] = dialog(MAKEINTRESOURCE(IDD_APPEXIT), window, tab_dlg); + ShowWindow(tablist[NSSM_TAB_EXIT], SW_HIDE); + + /* Set defaults. */ + SetDlgItemInt(tablist[NSSM_TAB_EXIT], IDC_THROTTLE, NSSM_RESET_THROTTLE_RESTART, 0); + combo = GetDlgItem(tablist[NSSM_TAB_EXIT], IDC_APPEXIT); + SendMessage(combo, CB_INSERTSTRING, NSSM_EXIT_RESTART, (LPARAM) message_string(NSSM_GUI_EXIT_RESTART)); + SendMessage(combo, CB_INSERTSTRING, NSSM_EXIT_IGNORE, (LPARAM) message_string(NSSM_GUI_EXIT_IGNORE)); + SendMessage(combo, CB_INSERTSTRING, NSSM_EXIT_REALLY, (LPARAM) message_string(NSSM_GUI_EXIT_REALLY)); + SendMessage(combo, CB_INSERTSTRING, NSSM_EXIT_UNCLEAN, (LPARAM) message_string(NSSM_GUI_EXIT_UNCLEAN)); + SendMessage(combo, CB_SETCURSEL, NSSM_EXIT_RESTART, 0); + SetDlgItemInt(tablist[NSSM_TAB_EXIT], IDC_RESTART_DELAY, 0, 0); + + /* I/O tab. */ + tab.pszText = message_string(NSSM_GUI_TAB_IO); + tab.cchTextMax = (int) _tcslen(tab.pszText) + 1; + SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_IO, (LPARAM) &tab); + tablist[NSSM_TAB_IO] = dialog(MAKEINTRESOURCE(IDD_IO), window, tab_dlg); + ShowWindow(tablist[NSSM_TAB_IO], SW_HIDE); + + /* Rotation tab. */ + tab.pszText = message_string(NSSM_GUI_TAB_ROTATION); + tab.cchTextMax = (int) _tcslen(tab.pszText) + 1; + SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_ROTATION, (LPARAM) &tab); + tablist[NSSM_TAB_ROTATION] = dialog(MAKEINTRESOURCE(IDD_ROTATION), window, tab_dlg); + ShowWindow(tablist[NSSM_TAB_ROTATION], SW_HIDE); + + /* Set defaults. */ + SendDlgItemMessage(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_ONLINE, BM_SETCHECK, BST_UNCHECKED, 0); + SetDlgItemInt(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_SECONDS, 0, 0); + SetDlgItemInt(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_BYTES_LOW, 0, 0); + set_rotation_enabled(0); + + /* Environment tab. */ + tab.pszText = message_string(NSSM_GUI_TAB_ENVIRONMENT); + tab.cchTextMax = (int) _tcslen(tab.pszText) + 1; + SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_ENVIRONMENT, (LPARAM) &tab); + tablist[NSSM_TAB_ENVIRONMENT] = dialog(MAKEINTRESOURCE(IDD_ENVIRONMENT), window, tab_dlg); + ShowWindow(tablist[NSSM_TAB_ENVIRONMENT], SW_HIDE); + + return 1; + + /* Tab change. */ + case WM_NOTIFY: + NMHDR *notification; + + notification = (NMHDR *) l; + switch (notification->code) { + case TCN_SELCHANGE: + HWND tabs; + int selection; + + tabs = GetDlgItem(window, IDC_TAB1); + if (! tabs) return 0; + + selection = (int) SendMessage(tabs, TCM_GETCURSEL, 0, 0); + if (selection != selected_tab) { + ShowWindow(tablist[selected_tab], SW_HIDE); + ShowWindow(tablist[selection], SW_SHOWDEFAULT); + SetFocus(GetDlgItem(window, IDOK)); + selected_tab = selection; + } + return 1; + } + + return 0; + + /* Button was pressed or control was controlled */ + case WM_COMMAND: + switch (LOWORD(w)) { + /* OK button */ + case IDOK: + if ((int) GetWindowLongPtr(window, GWLP_USERDATA) == IDD_EDIT) { + if (! edit(window, (nssm_service_t *) GetWindowLongPtr(window, DWLP_USER))) PostQuitMessage(0); + } + else if (! install(window)) PostQuitMessage(0); + break; + + /* Cancel button */ + case IDCANCEL: + DestroyWindow(window); + break; + + /* Remove button */ + case IDC_REMOVE: + if (! remove(window)) PostQuitMessage(0); + break; + } + return 1; + + /* Window closing */ + case WM_CLOSE: + DestroyWindow(window); + return 0; + case WM_DESTROY: + PostQuitMessage(0); + } + return 0; +} diff --git a/Static/influxDB/nssm-2.24/src/gui.h b/Static/influxDB/nssm-2.24/src/gui.h new file mode 100644 index 0000000..7c13c25 --- /dev/null +++ b/Static/influxDB/nssm-2.24/src/gui.h @@ -0,0 +1,18 @@ +#ifndef GUI_H +#define GUI_H + +#include +#include +#include +#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 diff --git a/Static/influxDB/nssm-2.24/src/imports.cpp b/Static/influxDB/nssm-2.24/src/imports.cpp new file mode 100644 index 0000000..6257131 --- /dev/null +++ b/Static/influxDB/nssm-2.24/src/imports.cpp @@ -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)); +} diff --git a/Static/influxDB/nssm-2.24/src/imports.h b/Static/influxDB/nssm-2.24/src/imports.h new file mode 100644 index 0000000..03e2df1 --- /dev/null +++ b/Static/influxDB/nssm-2.24/src/imports.h @@ -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 diff --git a/Static/influxDB/nssm-2.24/src/io.cpp b/Static/influxDB/nssm-2.24/src/io.cpp new file mode 100644 index 0000000..d311274 --- /dev/null +++ b/Static/influxDB/nssm-2.24/src/io.cpp @@ -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; +} diff --git a/Static/influxDB/nssm-2.24/src/io.h b/Static/influxDB/nssm-2.24/src/io.h new file mode 100644 index 0000000..cba380b --- /dev/null +++ b/Static/influxDB/nssm-2.24/src/io.h @@ -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 diff --git a/Static/influxDB/nssm-2.24/src/messages.mc b/Static/influxDB/nssm-2.24/src/messages.mc new file mode 100644 index 0000000..8a06c12 Binary files /dev/null and b/Static/influxDB/nssm-2.24/src/messages.mc differ diff --git a/Static/influxDB/nssm-2.24/src/nssm.cpp b/Static/influxDB/nssm-2.24/src/nssm.cpp new file mode 100644 index 0000000..260ee21 --- /dev/null +++ b/Static/influxDB/nssm-2.24/src/nssm.cpp @@ -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); +} diff --git a/Static/influxDB/nssm-2.24/src/nssm.h b/Static/influxDB/nssm-2.24/src/nssm.h new file mode 100644 index 0000000..9dc1486 --- /dev/null +++ b/Static/influxDB/nssm-2.24/src/nssm.h @@ -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 +#include +#include +#include +#include +#include +#include +#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 diff --git a/Static/influxDB/nssm-2.24/src/nssm.ico b/Static/influxDB/nssm-2.24/src/nssm.ico new file mode 100644 index 0000000..0b1db25 Binary files /dev/null and b/Static/influxDB/nssm-2.24/src/nssm.ico differ diff --git a/Static/influxDB/nssm-2.24/src/nssm.rc b/Static/influxDB/nssm-2.24/src/nssm.rc new file mode 100644 index 0000000..2d13624 Binary files /dev/null and b/Static/influxDB/nssm-2.24/src/nssm.rc differ diff --git a/Static/influxDB/nssm-2.24/src/nssm.sln b/Static/influxDB/nssm-2.24/src/nssm.sln new file mode 100644 index 0000000..f154427 --- /dev/null +++ b/Static/influxDB/nssm-2.24/src/nssm.sln @@ -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 diff --git a/Static/influxDB/nssm-2.24/src/nssm.vcproj b/Static/influxDB/nssm-2.24/src/nssm.vcproj new file mode 100644 index 0000000..c6a933c --- /dev/null +++ b/Static/influxDB/nssm-2.24/src/nssm.vcproj @@ -0,0 +1,793 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Static/influxDB/nssm-2.24/src/process.cpp b/Static/influxDB/nssm-2.24/src/process.cpp new file mode 100644 index 0000000..624111c --- /dev/null +++ b/Static/influxDB/nssm-2.24/src/process.cpp @@ -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); +} diff --git a/Static/influxDB/nssm-2.24/src/process.h b/Static/influxDB/nssm-2.24/src/process.h new file mode 100644 index 0000000..a79f9fd --- /dev/null +++ b/Static/influxDB/nssm-2.24/src/process.h @@ -0,0 +1,21 @@ +#ifndef PROCESS_H +#define PROCESS_H + +#include + +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 diff --git a/Static/influxDB/nssm-2.24/src/registry.cpp b/Static/influxDB/nssm-2.24/src/registry.cpp new file mode 100644 index 0000000..1c8f694 --- /dev/null +++ b/Static/influxDB/nssm-2.24/src/registry.cpp @@ -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; +} diff --git a/Static/influxDB/nssm-2.24/src/registry.h b/Static/influxDB/nssm-2.24/src/registry.h new file mode 100644 index 0000000..cb2b208 --- /dev/null +++ b/Static/influxDB/nssm-2.24/src/registry.h @@ -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 diff --git a/Static/influxDB/nssm-2.24/src/resource.h b/Static/influxDB/nssm-2.24/src/resource.h new file mode 100644 index 0000000..1e3500a --- /dev/null +++ b/Static/influxDB/nssm-2.24/src/resource.h @@ -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 + diff --git a/Static/influxDB/nssm-2.24/src/service.cpp b/Static/influxDB/nssm-2.24/src/service.cpp new file mode 100644 index 0000000..0bb5ff6 --- /dev/null +++ b/Static/influxDB/nssm-2.24/src/service.cpp @@ -0,0 +1,1976 @@ +#include "nssm.h" + +bool is_admin; +bool use_critical_section; + +extern imports_t imports; +extern settings_t settings[]; + +const TCHAR *exit_action_strings[] = { _T("Restart"), _T("Ignore"), _T("Exit"), _T("Suicide"), 0 }; +const TCHAR *startup_strings[] = { _T("SERVICE_AUTO_START"), _T("SERVICE_DELAYED_AUTO_START"), _T("SERVICE_DEMAND_START"), _T("SERVICE_DISABLED"), 0 }; +const TCHAR *priority_strings[] = { _T("REALTIME_PRIORITY_CLASS"), _T("HIGH_PRIORITY_CLASS"), _T("ABOVE_NORMAL_PRIORITY_CLASS"), _T("NORMAL_PRIORITY_CLASS"), _T("BELOW_NORMAL_PRIORITY_CLASS"), _T("IDLE_PRIORITY_CLASS"), 0 }; + +typedef struct { + int first; + int last; +} list_t; + +/* + Check the status in response to a control. + Returns: 1 if the status is expected, eg STOP following CONTROL_STOP. + 0 if the status is desired, eg STOPPED following CONTROL_STOP. + -1 if the status is undesired, eg STOPPED following CONTROL_START. +*/ +static inline int service_control_response(unsigned long control, unsigned long status) { + switch (control) { + case NSSM_SERVICE_CONTROL_START: + switch (status) { + case SERVICE_START_PENDING: + return 1; + + case SERVICE_RUNNING: + return 0; + + default: + return -1; + } + + case SERVICE_CONTROL_STOP: + case SERVICE_CONTROL_SHUTDOWN: + switch (status) { + case SERVICE_STOP_PENDING: + return 1; + + case SERVICE_STOPPED: + return 0; + + default: + return -1; + } + + case SERVICE_CONTROL_PAUSE: + switch (status) { + case SERVICE_PAUSE_PENDING: + return 1; + + case SERVICE_PAUSED: + return 0; + + default: + return -1; + } + + case SERVICE_CONTROL_CONTINUE: + switch (status) { + case SERVICE_CONTINUE_PENDING: + return 1; + + case SERVICE_RUNNING: + return 0; + + default: + return -1; + } + + case SERVICE_CONTROL_INTERROGATE: + return 0; + } + + return 0; +} + +static inline int await_service_control_response(unsigned long control, SC_HANDLE service_handle, SERVICE_STATUS *service_status, unsigned long initial_status) { + int tries = 0; + while (QueryServiceStatus(service_handle, service_status)) { + int response = service_control_response(control, service_status->dwCurrentState); + /* Alas we can't WaitForSingleObject() on an SC_HANDLE. */ + if (! response) return response; + if (response > 0 || service_status->dwCurrentState == initial_status) { + if (++tries > 10) return response; + Sleep(50 * tries); + } + else return response; + } + return -1; +} + +int affinity_mask_to_string(__int64 mask, TCHAR **string) { + if (! string) return 1; + if (! mask) { + *string = 0; + return 0; + } + + __int64 i, n; + + /* SetProcessAffinityMask() accepts a mask of up to 64 processors. */ + list_t set[64]; + for (n = 0; n < _countof(set); n++) set[n].first = set[n].last = -1; + + for (i = 0, n = 0; i < _countof(set); i++) { + if (mask & (1LL << i)) { + if (set[n].first == -1) set[n].first = set[n].last = (int) i; + else if (set[n].last == (int) i - 1) set[n].last = (int) i; + else { + n++; + set[n].first = set[n].last = (int) i; + } + } + } + + /* Worst case is 2x2 characters for first and last CPU plus - and/or , */ + size_t len = (size_t) (n + 1) * 6; + *string = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len * sizeof(TCHAR)); + if (! string) return 2; + + size_t s = 0; + int ret; + for (i = 0; i <= n; i++) { + if (i) (*string)[s++] = _T(','); + ret = _sntprintf_s(*string + s, 3, _TRUNCATE, _T("%u"), set[i].first); + if (ret < 0) { + HeapFree(GetProcessHeap(), 0, *string); + *string = 0; + return 3; + } + else s += ret; + if (set[i].last != set[i].first) { + ret =_sntprintf_s(*string + s, 4, _TRUNCATE, _T("%c%u"), (set[i].last == set[i].first + 1) ? _T(',') : _T('-'), set[i].last); + if (ret < 0) { + HeapFree(GetProcessHeap(), 0, *string); + *string = 0; + return 4; + } + else s += ret; + } + } + + return 0; +} + +int affinity_string_to_mask(TCHAR *string, __int64 *mask) { + if (! mask) return 1; + + *mask = 0LL; + if (! string) return 0; + + list_t set[64]; + + TCHAR *s = string; + TCHAR *end; + int ret; + int i; + int n = 0; + unsigned long number; + + for (n = 0; n < _countof(set); n++) set[n].first = set[n].last = -1; + n = 0; + + while (*s) { + ret = str_number(s, &number, &end); + s = end; + if (ret == 0 || ret == 2) { + if (number >= _countof(set)) return 2; + set[n].first = set[n].last = (int) number; + + switch (*s) { + case 0: + break; + + case _T(','): + n++; + s++; + break; + + case _T('-'): + if (! *(++s)) return 3; + ret = str_number(s, &number, &end); + if (ret == 0 || ret == 2) { + s = end; + if (! *s || *s == _T(',')) { + set[n].last = (int) number; + if (! *s) break; + n++; + s++; + } + else return 3; + } + else return 3; + break; + + default: + return 3; + } + } + else return 4; + } + + for (i = 0; i <= n; i++) { + for (int j = set[i].first; j <= set[i].last; j++) (__int64) *mask |= (1LL << (__int64) j); + } + + return 0; +} + +inline unsigned long priority_mask() { + return REALTIME_PRIORITY_CLASS | HIGH_PRIORITY_CLASS | ABOVE_NORMAL_PRIORITY_CLASS | NORMAL_PRIORITY_CLASS | BELOW_NORMAL_PRIORITY_CLASS | IDLE_PRIORITY_CLASS; +} + +int priority_constant_to_index(unsigned long constant) { + switch (constant & priority_mask()) { + case REALTIME_PRIORITY_CLASS: return NSSM_REALTIME_PRIORITY; + case HIGH_PRIORITY_CLASS: return NSSM_HIGH_PRIORITY; + case ABOVE_NORMAL_PRIORITY_CLASS: return NSSM_ABOVE_NORMAL_PRIORITY; + case BELOW_NORMAL_PRIORITY_CLASS: return NSSM_BELOW_NORMAL_PRIORITY; + case IDLE_PRIORITY_CLASS: return NSSM_IDLE_PRIORITY; + } + return NSSM_NORMAL_PRIORITY; +} + +unsigned long priority_index_to_constant(int index) { + switch (index) { + case NSSM_REALTIME_PRIORITY: return REALTIME_PRIORITY_CLASS; + case NSSM_HIGH_PRIORITY: return HIGH_PRIORITY_CLASS; + case NSSM_ABOVE_NORMAL_PRIORITY: return ABOVE_NORMAL_PRIORITY_CLASS; + case NSSM_BELOW_NORMAL_PRIORITY: return BELOW_NORMAL_PRIORITY_CLASS; + case NSSM_IDLE_PRIORITY: return IDLE_PRIORITY_CLASS; + } + return NORMAL_PRIORITY_CLASS; +} + +static inline unsigned long throttle_milliseconds(unsigned long throttle) { + /* pow() operates on doubles. */ + unsigned long ret = 1; for (unsigned long i = 1; i < throttle; i++) ret *= 2; + return ret * 1000; +} + +/* + Wrapper to be called in a new thread so that we can acknowledge a STOP + control immediately. +*/ +static unsigned long WINAPI shutdown_service(void *arg) { + return stop_service((nssm_service_t *) arg, 0, true, true); +} + +/* Connect to the service manager */ +SC_HANDLE open_service_manager(unsigned long access) { + SC_HANDLE ret = OpenSCManager(0, SERVICES_ACTIVE_DATABASE, access); + if (! ret) { + if (is_admin) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENSCMANAGER_FAILED, 0); + return 0; + } + + return ret; +} + +/* Open a service by name or display name. */ +SC_HANDLE open_service(SC_HANDLE services, TCHAR *service_name, unsigned long access, TCHAR *canonical_name, unsigned long canonical_namelen) { + SC_HANDLE service_handle = OpenService(services, service_name, access); + if (service_handle) { + if (canonical_name && canonical_name != service_name) { + TCHAR displayname[SERVICE_NAME_LENGTH]; + unsigned long displayname_len = (unsigned long) _countof(displayname); + GetServiceDisplayName(services, service_name, displayname, &displayname_len); + unsigned long keyname_len = canonical_namelen; + GetServiceKeyName(services, displayname, canonical_name, &keyname_len); + } + return service_handle; + } + + unsigned long error = GetLastError(); + if (error != ERROR_SERVICE_DOES_NOT_EXIST) { + print_message(stderr, NSSM_MESSAGE_OPENSERVICE_FAILED, error_string(GetLastError())); + return 0; + } + + /* We can't look for a display name because there's no buffer to store it. */ + if (! canonical_name) { + print_message(stderr, NSSM_MESSAGE_OPENSERVICE_FAILED, error_string(GetLastError())); + return 0; + } + + unsigned long bufsize, required, count, i; + unsigned long resume = 0; + EnumServicesStatus(services, SERVICE_DRIVER | SERVICE_FILE_SYSTEM_DRIVER | SERVICE_KERNEL_DRIVER | SERVICE_WIN32, SERVICE_STATE_ALL, 0, 0, &required, &count, &resume); + error = GetLastError(); + if (error != ERROR_MORE_DATA) { + print_message(stderr, NSSM_MESSAGE_ENUMSERVICESSTATUS_FAILED, error_string(GetLastError())); + return 0; + } + + ENUM_SERVICE_STATUS *status = (ENUM_SERVICE_STATUS *) HeapAlloc(GetProcessHeap(), 0, required); + if (! status) { + print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("ENUM_SERVICE_STATUS"), _T("open_service()")); + return 0; + } + + bufsize = required; + while (true) { + /* + EnumServicesStatus() returns: + 1 when it retrieved data and there's no more data to come. + 0 and sets last error to ERROR_MORE_DATA when it retrieved data and + there's more data to come. + 0 and sets last error to something else on error. + */ + int ret = EnumServicesStatus(services, SERVICE_DRIVER | SERVICE_FILE_SYSTEM_DRIVER | SERVICE_KERNEL_DRIVER | SERVICE_WIN32, SERVICE_STATE_ALL, status, bufsize, &required, &count, &resume); + if (! ret) { + error = GetLastError(); + if (error != ERROR_MORE_DATA) { + HeapFree(GetProcessHeap(), 0, status); + print_message(stderr, NSSM_MESSAGE_ENUMSERVICESSTATUS_FAILED, error_string(GetLastError())); + return 0; + } + } + + for (i = 0; i < count; i++) { + if (str_equiv(status[i].lpDisplayName, service_name)) { + if (_sntprintf_s(canonical_name, canonical_namelen, _TRUNCATE, _T("%s"), status[i].lpServiceName) < 0) { + HeapFree(GetProcessHeap(), 0, status); + print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canonical_name"), _T("open_service()")); + return 0; + } + + HeapFree(GetProcessHeap(), 0, status); + return open_service(services, canonical_name, access, 0, 0); + } + } + + if (ret) break; + } + + /* Recurse so we can get an error message. */ + return open_service(services, service_name, access, 0, 0); +} + +QUERY_SERVICE_CONFIG *query_service_config(const TCHAR *service_name, SC_HANDLE service_handle) { + QUERY_SERVICE_CONFIG *qsc; + unsigned long bufsize; + unsigned long error; + + QueryServiceConfig(service_handle, 0, 0, &bufsize); + error = GetLastError(); + if (error == ERROR_INSUFFICIENT_BUFFER) { + qsc = (QUERY_SERVICE_CONFIG *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, bufsize); + if (! qsc) { + print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("QUERY_SERVICE_CONFIG"), _T("query_service_config()"), 0); + return 0; + } + } + else { + print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG_FAILED, service_name, error_string(error), 0); + return 0; + } + + if (! QueryServiceConfig(service_handle, qsc, bufsize, &bufsize)) { + HeapFree(GetProcessHeap(), 0, qsc); + print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG_FAILED, service_name, error_string(GetLastError()), 0); + return 0; + } + + return qsc; +} + +int set_service_dependencies(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR *buffer) { + TCHAR *dependencies = _T(""); + unsigned long num_dependencies = 0; + + if (buffer && buffer[0]) { + SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE); + if (! services) { + print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED); + return 1; + } + + /* + Count the dependencies then allocate a buffer big enough for their + canonical names, ie n * SERVICE_NAME_LENGTH. + */ + TCHAR *s; + TCHAR *groups = 0; + for (s = buffer; *s; s++) { + num_dependencies++; + if (*s == SC_GROUP_IDENTIFIER) groups = s; + while (*s) s++; + } + + /* At least one dependency is a group so we need to verify them. */ + if (groups) { + HKEY key; + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, NSSM_REGISTRY_GROUPS, 0, KEY_READ, &key)) { + _ftprintf(stderr, _T("%s: %s\n"), NSSM_REGISTRY_GROUPS, error_string(GetLastError())); + return 2; + } + + unsigned long type; + unsigned long groupslen; + unsigned long ret = RegQueryValueEx(key, NSSM_REG_GROUPS, 0, &type, NULL, &groupslen); + if (ret == ERROR_SUCCESS) { + groups = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, groupslen); + if (! groups) { + print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("groups"), _T("set_service_dependencies()")); + return 3; + } + + ret = RegQueryValueEx(key, NSSM_REG_GROUPS, 0, &type, (unsigned char *) groups, &groupslen); + if (ret != ERROR_SUCCESS) { + _ftprintf(stderr, _T("%s\\%s: %s"), NSSM_REGISTRY_GROUPS, NSSM_REG_GROUPS, error_string(GetLastError())); + HeapFree(GetProcessHeap(), 0, groups); + RegCloseKey(key); + return 4; + } + } + else if (ret != ERROR_FILE_NOT_FOUND) { + _ftprintf(stderr, _T("%s\\%s: %s"), NSSM_REGISTRY_GROUPS, NSSM_REG_GROUPS, error_string(GetLastError())); + RegCloseKey(key); + return 4; + } + + RegCloseKey(key); + + } + + unsigned long dependencieslen = (num_dependencies * SERVICE_NAME_LENGTH) + 2; + dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dependencieslen * sizeof(TCHAR)); + size_t i = 0; + + TCHAR dependency[SERVICE_NAME_LENGTH]; + for (s = buffer; *s; s++) { + /* Group? */ + if (*s == SC_GROUP_IDENTIFIER) { + TCHAR *group = s + 1; + + bool ok = false; + if (*group) { + for (TCHAR *g = groups; *g; g++) { + if (str_equiv(g, group)) { + ok = true; + /* Set canonical name. */ + memmove(group, g, _tcslen(g) * sizeof(TCHAR)); + break; + } + + while (*g) g++; + } + } + + if (ok) _sntprintf_s(dependency, _countof(dependency), _TRUNCATE, _T("%s"), s); + else { + HeapFree(GetProcessHeap(), 0, dependencies); + if (groups) HeapFree(GetProcessHeap(), 0, groups); + _ftprintf(stderr, _T("%s: %s"), s, error_string(ERROR_SERVICE_DEPENDENCY_DELETED)); + return 5; + } + } + else { + SC_HANDLE dependency_handle = open_service(services, s, SERVICE_QUERY_STATUS, dependency, _countof(dependency)); + if (! dependency_handle) { + HeapFree(GetProcessHeap(), 0, dependencies); + if (groups) HeapFree(GetProcessHeap(), 0, groups); + CloseServiceHandle(services); + _ftprintf(stderr, _T("%s: %s"), s, error_string(ERROR_SERVICE_DEPENDENCY_DELETED)); + return 5; + } + } + + size_t len = _tcslen(dependency) + 1; + memmove(dependencies + i, dependency, len * sizeof(TCHAR)); + i += len; + + while (*s) s++; + } + + if (groups) HeapFree(GetProcessHeap(), 0, groups); + CloseServiceHandle(services); + } + + if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, dependencies, 0, 0, 0)) { + if (num_dependencies) HeapFree(GetProcessHeap(), 0, dependencies); + print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError())); + return -1; + } + + if (num_dependencies) HeapFree(GetProcessHeap(), 0, dependencies); + return 0; +} + +int get_service_dependencies(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR **buffer, unsigned long *bufsize, int type) { + if (! buffer) return 1; + if (! bufsize) return 2; + + *buffer = 0; + *bufsize = 0; + + QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle); + if (! qsc) return 3; + + if (! qsc->lpDependencies) return 0; + if (! qsc->lpDependencies[0]) return 0; + + /* lpDependencies is doubly NULL terminated. */ + while (qsc->lpDependencies[*bufsize]) { + while (qsc->lpDependencies[*bufsize]) ++*bufsize; + ++*bufsize; + } + + *bufsize += 2; + + *buffer = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, *bufsize * sizeof(TCHAR)); + if (! *buffer) { + *bufsize = 0; + print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("lpDependencies"), _T("get_service_dependencies()")); + return 4; + } + + if (type == DEPENDENCY_ALL) memmove(*buffer, qsc->lpDependencies, *bufsize * sizeof(TCHAR)); + else { + TCHAR *s; + size_t i = 0; + *bufsize = 0; + for (s = qsc->lpDependencies; *s; s++) { + /* Only copy the appropriate type of dependency. */ + if ((*s == SC_GROUP_IDENTIFIER && type & DEPENDENCY_GROUPS) || (*s != SC_GROUP_IDENTIFIER && type & DEPENDENCY_SERVICES)) { + size_t len = _tcslen(s) + 1; + *bufsize += (unsigned long) len; + memmove(*buffer + i, s, len * sizeof(TCHAR)); + i += len; + } + + while (*s) s++; + } + ++*bufsize; + } + + HeapFree(GetProcessHeap(), 0, qsc); + + return 0; +} + +int get_service_dependencies(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR **buffer, unsigned long *bufsize) { + return get_service_dependencies(service_name, service_handle, buffer, bufsize, DEPENDENCY_ALL); +} + +int set_service_description(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR *buffer) { + SERVICE_DESCRIPTION description; + ZeroMemory(&description, sizeof(description)); + /* + lpDescription must be NULL if we aren't changing, the new description + or "". + */ + if (buffer && buffer[0]) description.lpDescription = buffer; + else description.lpDescription = _T(""); + + if (ChangeServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, &description)) return 0; + + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DESCRIPTION_FAILED, service_name, error_string(GetLastError()), 0); + return 1; +} + +int get_service_description(const TCHAR *service_name, SC_HANDLE service_handle, unsigned long len, TCHAR *buffer) { + if (! buffer) return 1; + + unsigned long bufsize; + QueryServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, 0, 0, &bufsize); + unsigned long error = GetLastError(); + if (error == ERROR_INSUFFICIENT_BUFFER) { + SERVICE_DESCRIPTION *description = (SERVICE_DESCRIPTION *) HeapAlloc(GetProcessHeap(), 0, bufsize); + if (! description) { + print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SERVICE_CONFIG_DESCRIPTION"), _T("get_service_description()")); + return 2; + } + + if (QueryServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, (unsigned char *) description, bufsize, &bufsize)) { + if (description->lpDescription) _sntprintf_s(buffer, len, _TRUNCATE, _T("%s"), description->lpDescription); + else ZeroMemory(buffer, len * sizeof(TCHAR)); + HeapFree(GetProcessHeap(), 0, description); + return 0; + } + else { + HeapFree(GetProcessHeap(), 0, description); + print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DESCRIPTION"), error_string(error)); + return 3; + } + } + else { + print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DESCRIPTION"), error_string(error)); + return 4; + } + + return 0; +} + +int get_service_startup(const TCHAR *service_name, SC_HANDLE service_handle, const QUERY_SERVICE_CONFIG *qsc, unsigned long *startup) { + if (! qsc) return 1; + + switch (qsc->dwStartType) { + case SERVICE_DEMAND_START: *startup = NSSM_STARTUP_MANUAL; break; + case SERVICE_DISABLED: *startup = NSSM_STARTUP_DISABLED; break; + default: *startup = NSSM_STARTUP_AUTOMATIC; + } + + if (*startup != NSSM_STARTUP_AUTOMATIC) return 0; + + /* Check for delayed start. */ + unsigned long bufsize; + unsigned long error; + QueryServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, 0, 0, &bufsize); + error = GetLastError(); + if (error == ERROR_INSUFFICIENT_BUFFER) { + SERVICE_DELAYED_AUTO_START_INFO *info = (SERVICE_DELAYED_AUTO_START_INFO *) HeapAlloc(GetProcessHeap(), 0, bufsize); + if (! info) { + print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SERVICE_DELAYED_AUTO_START_INFO"), _T("get_service_startup()")); + return 2; + } + + if (QueryServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, (unsigned char *) info, bufsize, &bufsize)) { + if (info->fDelayedAutostart) *startup = NSSM_STARTUP_DELAYED; + HeapFree(GetProcessHeap(), 0, info); + return 0; + } + else { + error = GetLastError(); + if (error != ERROR_INVALID_LEVEL) { + print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DELAYED_AUTO_START_INFO"), error_string(error)); + return 3; + } + } + } + else if (error != ERROR_INVALID_LEVEL) { + print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_DELAYED_AUTO_START_INFO"), error_string(error)); + return 3; + } + + return 0; +} + +int get_service_username(const TCHAR *service_name, const QUERY_SERVICE_CONFIG *qsc, TCHAR **username, size_t *usernamelen) { + if (! username) return 1; + if (! usernamelen) return 1; + + *username = 0; + *usernamelen = 0; + + if (! qsc) return 1; + + if (qsc->lpServiceStartName[0]) { + if (is_localsystem(qsc->lpServiceStartName)) return 0; + + size_t len = _tcslen(qsc->lpServiceStartName); + *username = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(TCHAR)); + if (! *username) { + print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("username"), _T("get_service_username()")); + return 2; + } + + memmove(*username, qsc->lpServiceStartName, (len + 1) * sizeof(TCHAR)); + *usernamelen = len; + } + + return 0; +} + +/* Set default values which aren't zero. */ +void set_nssm_service_defaults(nssm_service_t *service) { + if (! service) return; + + service->type = SERVICE_WIN32_OWN_PROCESS; + service->priority = NORMAL_PRIORITY_CLASS; + service->stdin_sharing = NSSM_STDIN_SHARING; + service->stdin_disposition = NSSM_STDIN_DISPOSITION; + service->stdin_flags = NSSM_STDIN_FLAGS; + service->stdout_sharing = NSSM_STDOUT_SHARING; + service->stdout_disposition = NSSM_STDOUT_DISPOSITION; + service->stdout_flags = NSSM_STDOUT_FLAGS; + service->stderr_sharing = NSSM_STDERR_SHARING; + service->stderr_disposition = NSSM_STDERR_DISPOSITION; + service->stderr_flags = NSSM_STDERR_FLAGS; + service->throttle_delay = NSSM_RESET_THROTTLE_RESTART; + service->stop_method = ~0; + service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD; + service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD; + service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD; +} + +/* Allocate and zero memory for a service. */ +nssm_service_t *alloc_nssm_service() { + nssm_service_t *service = (nssm_service_t *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(nssm_service_t)); + if (! service) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service"), _T("alloc_nssm_service()"), 0); + return service; +} + +/* Free memory for a service. */ +void cleanup_nssm_service(nssm_service_t *service) { + if (! service) return; + if (service->username) HeapFree(GetProcessHeap(), 0, service->username); + if (service->password) { + SecureZeroMemory(service->password, service->passwordlen); + HeapFree(GetProcessHeap(), 0, service->password); + } + if (service->dependencies) HeapFree(GetProcessHeap(), 0, service->dependencies); + if (service->env) HeapFree(GetProcessHeap(), 0, service->env); + if (service->env_extra) HeapFree(GetProcessHeap(), 0, service->env_extra); + if (service->handle) CloseServiceHandle(service->handle); + if (service->process_handle) CloseHandle(service->process_handle); + if (service->wait_handle) UnregisterWait(service->wait_handle); + if (service->throttle_section_initialised) DeleteCriticalSection(&service->throttle_section); + if (service->throttle_timer) CloseHandle(service->throttle_timer); + if (service->initial_env) FreeEnvironmentStrings(service->initial_env); + HeapFree(GetProcessHeap(), 0, service); +} + +/* About to install the service */ +int pre_install_service(int argc, TCHAR **argv) { + nssm_service_t *service = alloc_nssm_service(); + set_nssm_service_defaults(service); + if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]); + + /* Show the dialogue box if we didn't give the service name and path */ + if (argc < 2) return nssm_gui(IDD_INSTALL, service); + + if (! service) { + print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("service"), _T("pre_install_service()")); + return 1; + } + _sntprintf_s(service->exe, _countof(service->exe), _TRUNCATE, _T("%s"), argv[1]); + + /* Arguments are optional */ + size_t flagslen = 0; + size_t s = 0; + int i; + for (i = 2; i < argc; i++) flagslen += _tcslen(argv[i]) + 1; + if (! flagslen) flagslen = 1; + if (flagslen > _countof(service->flags)) { + print_message(stderr, NSSM_MESSAGE_FLAGS_TOO_LONG); + return 2; + } + + for (i = 2; i < argc; i++) { + size_t len = _tcslen(argv[i]); + memmove(service->flags + s, argv[i], len * sizeof(TCHAR)); + s += len; + if (i < argc - 1) service->flags[s++] = _T(' '); + } + + /* Work out directory name */ + _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe); + strip_basename(service->dir); + + int ret = install_service(service); + cleanup_nssm_service(service); + return ret; +} + +/* About to edit the service. */ +int pre_edit_service(int argc, TCHAR **argv) { + /* Require service name. */ + if (argc < 2) return usage(1); + + /* Are we editing on the command line? */ + enum { MODE_EDITING, MODE_GETTING, MODE_SETTING, MODE_RESETTING } mode = MODE_EDITING; + const TCHAR *verb = argv[0]; + const TCHAR *service_name = argv[1]; + bool getting = false; + bool unsetting = false; + + /* Minimum number of arguments. */ + int mandatory = 2; + /* Index of first value. */ + int remainder = 3; + int i; + if (str_equiv(verb, _T("get"))) { + mandatory = 3; + mode = MODE_GETTING; + } + else if (str_equiv(verb, _T("set"))) { + mandatory = 4; + mode = MODE_SETTING; + } + else if (str_equiv(verb, _T("reset")) || str_equiv(verb, _T("unset"))) { + mandatory = 3; + mode = MODE_RESETTING; + } + if (argc < mandatory) return usage(1); + + const TCHAR *parameter = 0; + settings_t *setting = 0; + TCHAR *additional; + + /* Validate the parameter. */ + if (mandatory > 2) { + bool additional_mandatory = false; + + parameter = argv[2]; + for (i = 0; settings[i].name; i++) { + setting = &settings[i]; + if (! str_equiv(setting->name, parameter)) continue; + if (((setting->additional & ADDITIONAL_GETTING) && mode == MODE_GETTING) || ((setting->additional & ADDITIONAL_SETTING) && mode == MODE_SETTING) || ((setting->additional & ADDITIONAL_RESETTING) && mode == MODE_RESETTING)) { + additional_mandatory = true; + mandatory++; + } + break; + } + if (! settings[i].name) { + print_message(stderr, NSSM_MESSAGE_INVALID_PARAMETER, parameter); + for (i = 0; settings[i].name; i++) _ftprintf(stderr, _T("%s\n"), settings[i].name); + return 1; + } + + additional = 0; + if (additional_mandatory) { + if (argc < mandatory) { + print_message(stderr, NSSM_MESSAGE_MISSING_SUBPARAMETER, parameter); + return 1; + } + additional = argv[3]; + remainder = 4; + } + else if (str_equiv(setting->name, NSSM_NATIVE_OBJECTNAME) && mode == MODE_SETTING) { + additional = argv[3]; + remainder = 4; + } + else { + additional = argv[remainder]; + if (argc < mandatory) return usage(1); + } + } + + nssm_service_t *service = alloc_nssm_service(); + _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), service_name); + + /* Open service manager */ + SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE); + if (! services) { + print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED); + return 2; + } + + /* Try to open the service */ + unsigned long access = SERVICE_QUERY_CONFIG; + if (mode != MODE_GETTING) access |= SERVICE_CHANGE_CONFIG; + service->handle = open_service(services, service->name, access, service->name, _countof(service->name)); + if (! service->handle) { + CloseServiceHandle(services); + return 3; + } + + /* Get system details. */ + QUERY_SERVICE_CONFIG *qsc = query_service_config(service->name, service->handle); + if (! qsc) { + CloseServiceHandle(service->handle); + CloseServiceHandle(services); + return 4; + } + + service->type = qsc->dwServiceType; + if (! (service->type & SERVICE_WIN32_OWN_PROCESS)) { + if (mode != MODE_GETTING) { + HeapFree(GetProcessHeap(), 0, qsc); + CloseServiceHandle(service->handle); + CloseServiceHandle(services); + print_message(stderr, NSSM_MESSAGE_CANNOT_EDIT, service->name, NSSM_WIN32_OWN_PROCESS, 0); + return 3; + } + } + + if (get_service_startup(service->name, service->handle, qsc, &service->startup)) { + if (mode != MODE_GETTING) { + HeapFree(GetProcessHeap(), 0, qsc); + CloseServiceHandle(service->handle); + CloseServiceHandle(services); + return 4; + } + } + + if (get_service_username(service->name, qsc, &service->username, &service->usernamelen)) { + if (mode != MODE_GETTING) { + HeapFree(GetProcessHeap(), 0, qsc); + CloseServiceHandle(service->handle); + CloseServiceHandle(services); + return 5; + } + } + + _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), qsc->lpDisplayName); + + /* Get the canonical service name. We open it case insensitively. */ + unsigned long bufsize = _countof(service->name); + GetServiceKeyName(services, service->displayname, service->name, &bufsize); + + /* Remember the executable in case it isn't NSSM. */ + _sntprintf_s(service->image, _countof(service->image), _TRUNCATE, _T("%s"), qsc->lpBinaryPathName); + HeapFree(GetProcessHeap(), 0, qsc); + + /* Get extended system details. */ + if (get_service_description(service->name, service->handle, _countof(service->description), service->description)) { + if (mode != MODE_GETTING) { + CloseServiceHandle(service->handle); + CloseServiceHandle(services); + return 6; + } + } + + if (get_service_dependencies(service->name, service->handle, &service->dependencies, &service->dependencieslen)) { + if (mode != MODE_GETTING) { + CloseServiceHandle(service->handle); + CloseServiceHandle(services); + return 7; + } + } + + /* Get NSSM details. */ + get_parameters(service, 0); + + CloseServiceHandle(services); + + if (! service->exe[0]) { + service->native = true; + if (mode != MODE_GETTING) print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE, service->name, NSSM, service->image); + } + + /* Editing with the GUI. */ + if (mode == MODE_EDITING) { + nssm_gui(IDD_EDIT, service); + return 0; + } + + /* Trying to manage App* parameters for a non-NSSM service. */ + if (! setting->native && service->native) { + CloseServiceHandle(service->handle); + print_message(stderr, NSSM_MESSAGE_NATIVE_PARAMETER, setting->name, NSSM); + return 1; + } + + HKEY key; + value_t value; + int ret; + + if (mode == MODE_GETTING) { + if (! service->native) { + key = open_registry(service->name, KEY_READ); + if (! key) return 4; + } + + if (setting->native) ret = get_setting(service->name, service->handle, setting, &value, additional); + else ret = get_setting(service->name, key, setting, &value, additional); + if (ret < 0) { + CloseServiceHandle(service->handle); + return 5; + } + + switch (setting->type) { + case REG_EXPAND_SZ: + case REG_MULTI_SZ: + case REG_SZ: + _tprintf(_T("%s\n"), value.string ? value.string : _T("")); + HeapFree(GetProcessHeap(), 0, value.string); + break; + + case REG_DWORD: + _tprintf(_T("%u\n"), value.numeric); + break; + } + + if (! service->native) RegCloseKey(key); + CloseServiceHandle(service->handle); + return 0; + } + + /* Build the value. */ + if (mode == MODE_RESETTING) { + /* Unset the parameter. */ + value.string = 0; + } + else if (remainder == argc) { + value.string = 0; + } + else { + /* Set the parameter. */ + size_t len = 0; + size_t delimiterlen = (setting->additional & ADDITIONAL_CRLF) ? 2 : 1; + for (i = remainder; i < argc; i++) len += _tcslen(argv[i]) + delimiterlen; + len++; + + value.string = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR)); + if (! value.string) { + print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("value"), _T("edit_service()")); + CloseServiceHandle(service->handle); + return 2; + } + + size_t s = 0; + for (i = remainder; i < argc; i++) { + size_t len = _tcslen(argv[i]); + memmove(value.string + s, argv[i], len * sizeof(TCHAR)); + s += len; + if (i < argc - 1) { + if (setting->additional & ADDITIONAL_CRLF) { + value.string[s++] = _T('\r'); + value.string[s++] = _T('\n'); + } + else value.string[s++] = _T(' '); + } + } + value.string[s] = _T('\0'); + } + + if (! service->native) { + key = open_registry(service->name, KEY_WRITE); + if (! key) { + if (value.string) HeapFree(GetProcessHeap(), 0, value.string); + return 4; + } + } + + if (setting->native) ret = set_setting(service->name, service->handle, setting, &value, additional); + else ret = set_setting(service->name, key, setting, &value, additional); + if (value.string) HeapFree(GetProcessHeap(), 0, value.string); + if (ret < 0) { + if (! service->native) RegCloseKey(key); + CloseServiceHandle(service->handle); + return 6; + } + + if (! service->native) RegCloseKey(key); + CloseServiceHandle(service->handle); + + return 0; +} + +/* About to remove the service */ +int pre_remove_service(int argc, TCHAR **argv) { + nssm_service_t *service = alloc_nssm_service(); + set_nssm_service_defaults(service); + if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]); + + /* Show dialogue box if we didn't pass service name and "confirm" */ + if (argc < 2) return nssm_gui(IDD_REMOVE, service); + if (str_equiv(argv[1], _T("confirm"))) { + int ret = remove_service(service); + cleanup_nssm_service(service); + return ret; + } + print_message(stderr, NSSM_MESSAGE_PRE_REMOVE_SERVICE); + return 100; +} + +/* Install the service */ +int install_service(nssm_service_t *service) { + if (! service) return 1; + + /* Open service manager */ + SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE); + if (! services) { + print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED); + cleanup_nssm_service(service); + return 2; + } + + /* Get path of this program */ + GetModuleFileName(0, service->image, _countof(service->image)); + + /* Create the service - settings will be changed in edit_service() */ + service->handle = CreateService(services, service->name, service->name, SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, service->image, 0, 0, 0, 0, 0); + if (! service->handle) { + print_message(stderr, NSSM_MESSAGE_CREATESERVICE_FAILED, error_string(GetLastError())); + CloseServiceHandle(services); + return 5; + } + + if (edit_service(service, false)) { + DeleteService(service->handle); + CloseServiceHandle(services); + return 6; + } + + print_message(stdout, NSSM_MESSAGE_SERVICE_INSTALLED, service->name); + + /* Cleanup */ + CloseServiceHandle(services); + + return 0; +} + +/* Edit the service. */ +int edit_service(nssm_service_t *service, bool editing) { + if (! service) return 1; + + /* + The only two valid flags for service type are SERVICE_WIN32_OWN_PROCESS + and SERVICE_INTERACTIVE_PROCESS. + */ + service->type &= SERVICE_INTERACTIVE_PROCESS; + service->type |= SERVICE_WIN32_OWN_PROCESS; + + /* Startup type. */ + unsigned long startup; + switch (service->startup) { + case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break; + case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break; + default: startup = SERVICE_AUTO_START; + } + + /* Display name. */ + if (! service->displayname[0]) _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), service->name); + + /* + Username must be NULL if we aren't changing or an account name. + We must explicitly use LOCALSYSTEM to change it when we are editing. + Password must be NULL if we aren't changing, a password or "". + Empty passwords are valid but we won't allow them in the GUI. + */ + TCHAR *username = 0; + TCHAR *canon = 0; + TCHAR *password = 0; + if (service->usernamelen) { + username = service->username; + if (canonicalise_username(username, &canon)) return 5; + if (service->passwordlen) password = service->password; + } + else if (editing) username = canon = NSSM_LOCALSYSTEM_ACCOUNT; + + if (well_known_username(canon)) password = _T(""); + else { + if (grant_logon_as_service(canon)) { + if (canon != username) HeapFree(GetProcessHeap(), 0, canon); + print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username); + return 5; + } + } + + TCHAR *dependencies = _T(""); + if (service->dependencieslen) dependencies = 0; /* Change later. */ + + if (! ChangeServiceConfig(service->handle, service->type, startup, SERVICE_NO_CHANGE, 0, 0, 0, dependencies, canon, password, service->displayname)) { + if (canon != username) HeapFree(GetProcessHeap(), 0, canon); + print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError())); + return 5; + } + if (canon != username) HeapFree(GetProcessHeap(), 0, canon); + + if (service->dependencieslen) { + if (set_service_dependencies(service->name, service->handle, service->dependencies)) return 5; + } + + if (service->description[0] || editing) { + set_service_description(service->name, service->handle, service->description); + } + + SERVICE_DELAYED_AUTO_START_INFO delayed; + ZeroMemory(&delayed, sizeof(delayed)); + if (service->startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1; + else delayed.fDelayedAutostart = 0; + /* Delayed startup isn't supported until Vista. */ + if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) { + unsigned long error = GetLastError(); + /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */ + if (error != ERROR_INVALID_LEVEL) { + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service->name, error_string(error), 0); + } + } + + /* Don't mess with parameters which aren't ours. */ + if (! service->native) { + /* Now we need to put the parameters into the registry */ + if (create_parameters(service, editing)) { + print_message(stderr, NSSM_MESSAGE_CREATE_PARAMETERS_FAILED); + return 6; + } + + set_service_recovery(service); + } + + return 0; +} + +/* Control a service. */ +int control_service(unsigned long control, int argc, TCHAR **argv) { + if (argc < 1) return usage(1); + TCHAR *service_name = argv[0]; + TCHAR canonical_name[SERVICE_NAME_LENGTH]; + + SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE); + if (! services) { + print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED); + return 2; + } + + unsigned long access = SERVICE_QUERY_STATUS; + switch (control) { + case NSSM_SERVICE_CONTROL_START: + access |= SERVICE_START; + break; + + case SERVICE_CONTROL_CONTINUE: + case SERVICE_CONTROL_PAUSE: + access |= SERVICE_PAUSE_CONTINUE; + break; + + case SERVICE_CONTROL_STOP: + access |= SERVICE_STOP; + break; + + case NSSM_SERVICE_CONTROL_ROTATE: + access |= SERVICE_USER_DEFINED_CONTROL; + break; + } + + SC_HANDLE service_handle = open_service(services, service_name, access, canonical_name, _countof(canonical_name)); + if (! service_handle) { + CloseServiceHandle(services); + return 3; + } + + int ret; + unsigned long error; + SERVICE_STATUS service_status; + if (control == NSSM_SERVICE_CONTROL_START) { + unsigned long initial_status = SERVICE_STOPPED; + ret = StartService(service_handle, (unsigned long) argc, (const TCHAR **) argv); + error = GetLastError(); + CloseServiceHandle(services); + + if (error == ERROR_IO_PENDING) { + /* + Older versions of Windows return immediately with ERROR_IO_PENDING + indicate that the operation is still in progress. Newer versions + will return it if there really is a delay. + */ + ret = 1; + error = ERROR_SUCCESS; + } + + if (ret) { + int response = await_service_control_response(control, service_handle, &service_status, initial_status); + CloseServiceHandle(service_handle); + + if (response) { + print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control)); + return 1; + } + else _tprintf(_T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error)); + return 0; + } + else { + CloseServiceHandle(service_handle); + _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error)); + return 1; + } + } + else if (control == SERVICE_CONTROL_INTERROGATE) { + /* + We could actually send an INTERROGATE control but that won't return + any information if the service is stopped and we don't care about + the extra details it might give us in any case. So we'll fake it. + */ + ret = QueryServiceStatus(service_handle, &service_status); + error = GetLastError(); + + if (ret) { + _tprintf(_T("%s\n"), service_status_text(service_status.dwCurrentState)); + return 0; + } + else { + _ftprintf(stderr, _T("%s: %s\n"), canonical_name, error_string(error)); + return 1; + } + } + else { + ret = ControlService(service_handle, control, &service_status); + unsigned long initial_status = service_status.dwCurrentState; + error = GetLastError(); + CloseServiceHandle(services); + + if (error == ERROR_IO_PENDING) { + ret = 1; + error = ERROR_SUCCESS; + } + + if (ret) { + int response = await_service_control_response(control, service_handle, &service_status, initial_status); + CloseServiceHandle(service_handle); + + if (response) { + print_message(stderr, NSSM_MESSAGE_BAD_CONTROL_RESPONSE, canonical_name, service_status_text(service_status.dwCurrentState), service_control_text(control)); + return 1; + } + else _tprintf(_T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error)); + return 0; + } + else { + CloseServiceHandle(service_handle); + _ftprintf(stderr, _T("%s: %s: %s"), canonical_name, service_control_text(control), error_string(error)); + if (error == ERROR_SERVICE_NOT_ACTIVE) { + if (control == SERVICE_CONTROL_SHUTDOWN || control == SERVICE_CONTROL_STOP) return 0; + } + return 1; + } + } +} + +/* Remove the service */ +int remove_service(nssm_service_t *service) { + if (! service) return 1; + + /* Open service manager */ + SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE); + if (! services) { + print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED); + return 2; + } + + /* Try to open the service */ + service->handle = open_service(services, service->name, DELETE, service->name, _countof(service->name)); + if (! service->handle) { + CloseServiceHandle(services); + return 3; + } + + /* Get the canonical service name. We open it case insensitively. */ + unsigned long bufsize = _countof(service->displayname); + GetServiceDisplayName(services, service->name, service->displayname, &bufsize); + bufsize = _countof(service->name); + GetServiceKeyName(services, service->displayname, service->name, &bufsize); + + /* Try to delete the service */ + if (! DeleteService(service->handle)) { + print_message(stderr, NSSM_MESSAGE_DELETESERVICE_FAILED); + CloseServiceHandle(services); + return 4; + } + + /* Cleanup */ + CloseServiceHandle(services); + + print_message(stdout, NSSM_MESSAGE_SERVICE_REMOVED, service->name); + return 0; +} + +/* Service initialisation */ +void WINAPI service_main(unsigned long argc, TCHAR **argv) { + nssm_service_t *service = alloc_nssm_service(); + if (! service) return; + + if (_sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]) < 0) { + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service->name"), _T("service_main()"), 0); + return; + } + + /* We can use a condition variable in a critical section on Vista or later. */ + if (imports.SleepConditionVariableCS && imports.WakeConditionVariable) use_critical_section = true; + else use_critical_section = false; + + /* Initialise status */ + ZeroMemory(&service->status, sizeof(service->status)); + service->status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS; + service->status.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE; + service->status.dwWin32ExitCode = NO_ERROR; + service->status.dwServiceSpecificExitCode = 0; + service->status.dwCheckPoint = 0; + service->status.dwWaitHint = NSSM_WAITHINT_MARGIN; + + /* Signal we AREN'T running the server */ + service->process_handle = 0; + service->pid = 0; + + /* Register control handler */ + service->status_handle = RegisterServiceCtrlHandlerEx(NSSM, service_control_handler, (void *) service); + if (! service->status_handle) { + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_REGISTERSERVICECTRLHANDER_FAILED, error_string(GetLastError()), 0); + return; + } + + log_service_control(service->name, 0, true); + + service->status.dwCurrentState = SERVICE_START_PENDING; + service->status.dwWaitHint = service->throttle_delay + NSSM_WAITHINT_MARGIN; + SetServiceStatus(service->status_handle, &service->status); + + if (is_admin) { + /* Try to create the exit action parameters; we don't care if it fails */ + create_exit_action(service->name, exit_action_strings[0], false); + + SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT); + if (services) { + service->handle = open_service(services, service->name, SERVICE_CHANGE_CONFIG, 0, 0); + set_service_recovery(service); + CloseServiceHandle(services); + } + } + + /* Used for signalling a resume if the service pauses when throttled. */ + if (use_critical_section) { + InitializeCriticalSection(&service->throttle_section); + service->throttle_section_initialised = true; + } + else { + service->throttle_timer = CreateWaitableTimer(0, 1, 0); + if (! service->throttle_timer) { + log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_CREATEWAITABLETIMER_FAILED, service->name, error_string(GetLastError()), 0); + } + } + + /* Remember our initial environment. */ + service->initial_env = GetEnvironmentStrings(); + + monitor_service(service); +} + +/* Make sure service recovery actions are taken where necessary */ +void set_service_recovery(nssm_service_t *service) { + SERVICE_FAILURE_ACTIONS_FLAG flag; + ZeroMemory(&flag, sizeof(flag)); + flag.fFailureActionsOnNonCrashFailures = true; + + /* This functionality was added in Vista so the call may fail */ + if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &flag)) { + unsigned long error = GetLastError(); + /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */ + if (error != ERROR_INVALID_LEVEL) { + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_FAILURE_ACTIONS_FAILED, service->name, error_string(error), 0); + } + } +} + +int monitor_service(nssm_service_t *service) { + /* Set service status to started */ + int ret = start_service(service); + if (ret) { + TCHAR code[16]; + _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%d"), ret); + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, service->exe, service->name, ret, 0); + return ret; + } + log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, service->exe, service->flags, service->name, service->dir, 0); + + /* Monitor service */ + if (! RegisterWaitForSingleObject(&service->wait_handle, service->process_handle, end_service, (void *) service, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) { + log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service->name, service->exe, error_string(GetLastError()), 0); + } + + return 0; +} + +TCHAR *service_control_text(unsigned long control) { + switch (control) { + /* HACK: there is no SERVICE_CONTROL_START constant */ + case NSSM_SERVICE_CONTROL_START: return _T("START"); + case SERVICE_CONTROL_STOP: return _T("STOP"); + case SERVICE_CONTROL_SHUTDOWN: return _T("SHUTDOWN"); + case SERVICE_CONTROL_PAUSE: return _T("PAUSE"); + case SERVICE_CONTROL_CONTINUE: return _T("CONTINUE"); + case SERVICE_CONTROL_INTERROGATE: return _T("INTERROGATE"); + case NSSM_SERVICE_CONTROL_ROTATE: return _T("ROTATE"); + default: return 0; + } +} + +TCHAR *service_status_text(unsigned long status) { + switch (status) { + case SERVICE_STOPPED: return _T("SERVICE_STOPPED"); + case SERVICE_START_PENDING: return _T("SERVICE_START_PENDING"); + case SERVICE_STOP_PENDING: return _T("SERVICE_STOP_PENDING"); + case SERVICE_RUNNING: return _T("SERVICE_RUNNING"); + case SERVICE_CONTINUE_PENDING: return _T("SERVICE_CONTINUE_PENDING"); + case SERVICE_PAUSE_PENDING: return _T("SERVICE_PAUSE_PENDING"); + case SERVICE_PAUSED: return _T("SERVICE_PAUSED"); + default: return 0; + } +} + +void log_service_control(TCHAR *service_name, unsigned long control, bool handled) { + TCHAR *text = service_control_text(control); + unsigned long event; + + if (! text) { + /* "0x" + 8 x hex + NULL */ + text = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, 11 * sizeof(TCHAR)); + if (! text) { + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0); + return; + } + if (_sntprintf_s(text, 11, _TRUNCATE, _T("0x%08x"), control) < 0) { + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0); + HeapFree(GetProcessHeap(), 0, text); + return; + } + + event = NSSM_EVENT_SERVICE_CONTROL_UNKNOWN; + } + else if (handled) event = NSSM_EVENT_SERVICE_CONTROL_HANDLED; + else event = NSSM_EVENT_SERVICE_CONTROL_NOT_HANDLED; + + log_event(EVENTLOG_INFORMATION_TYPE, event, service_name, text, 0); + + if (event == NSSM_EVENT_SERVICE_CONTROL_UNKNOWN) { + HeapFree(GetProcessHeap(), 0, text); + } +} + +/* Service control handler */ +unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) { + nssm_service_t *service = (nssm_service_t *) context; + + switch (control) { + case SERVICE_CONTROL_INTERROGATE: + /* We always keep the service status up-to-date so this is a no-op. */ + return NO_ERROR; + + case SERVICE_CONTROL_SHUTDOWN: + case SERVICE_CONTROL_STOP: + log_service_control(service->name, control, true); + /* + We MUST acknowledge the stop request promptly but we're committed to + waiting for the application to exit. Spawn a new thread to wait + while we acknowledge the request. + */ + if (! CreateThread(NULL, 0, shutdown_service, context, 0, NULL)) { + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0); + + /* + We couldn't create a thread to tidy up so we'll have to force the tidyup + to complete in time in this thread. + */ + service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD; + service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD; + service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD; + + stop_service(service, 0, true, true); + } + return NO_ERROR; + + case SERVICE_CONTROL_CONTINUE: + log_service_control(service->name, control, true); + service->throttle = 0; + if (use_critical_section) imports.WakeConditionVariable(&service->throttle_condition); + else { + if (! service->throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED; + ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime)); + SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0); + } + /* We can't continue if the application is running! */ + if (! service->process_handle) service->status.dwCurrentState = SERVICE_CONTINUE_PENDING; + service->status.dwWaitHint = throttle_milliseconds(service->throttle) + NSSM_WAITHINT_MARGIN; + log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service->name, 0); + SetServiceStatus(service->status_handle, &service->status); + return NO_ERROR; + + case SERVICE_CONTROL_PAUSE: + /* + We don't accept pause messages but it isn't possible to register + only for continue messages so we have to handle this case. + */ + log_service_control(service->name, control, false); + return ERROR_CALL_NOT_IMPLEMENTED; + + case NSSM_SERVICE_CONTROL_ROTATE: + log_service_control(service->name, control, true); + if (service->rotate_stdout_online == NSSM_ROTATE_ONLINE) service->rotate_stdout_online = NSSM_ROTATE_ONLINE_ASAP; + if (service->rotate_stderr_online == NSSM_ROTATE_ONLINE) service->rotate_stderr_online = NSSM_ROTATE_ONLINE_ASAP; + return NO_ERROR; + } + + /* Unknown control */ + log_service_control(service->name, control, false); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +/* Start the service */ +int start_service(nssm_service_t *service) { + service->stopping = false; + service->allow_restart = true; + + if (service->process_handle) return 0; + + /* Allocate a STARTUPINFO structure for a new process */ + STARTUPINFO si; + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + + /* Allocate a PROCESSINFO structure for the process */ + PROCESS_INFORMATION pi; + ZeroMemory(&pi, sizeof(pi)); + + /* Get startup parameters */ + int ret = get_parameters(service, &si); + if (ret) { + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service->name, 0); + return stop_service(service, 2, true, true); + } + + /* Launch executable with arguments */ + TCHAR cmd[CMD_LENGTH]; + if (_sntprintf_s(cmd, _countof(cmd), _TRUNCATE, _T("\"%s\" %s"), service->exe, service->flags) < 0) { + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("command line"), _T("start_service"), 0); + return stop_service(service, 2, true, true); + } + + throttle_restart(service); + + /* Set the environment. */ + if (service->env) duplicate_environment(service->env); + if (service->env_extra) set_environment_block(service->env_extra); + + /* Set up I/O redirection. */ + if (get_output_handles(service, &si)) { + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0); + if (! service->no_console) FreeConsole(); + close_output_handles(&si); + return stop_service(service, 4, true, true); + } + + bool inherit_handles = false; + if (si.dwFlags & STARTF_USESTDHANDLES) inherit_handles = true; + unsigned long flags = service->priority & priority_mask(); + if (service->affinity) flags |= CREATE_SUSPENDED; + if (! CreateProcess(0, cmd, 0, 0, inherit_handles, flags, 0, service->dir, &si, &pi)) { + unsigned long exitcode = 3; + unsigned long error = GetLastError(); + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service->name, service->exe, error_string(error), 0); + close_output_handles(&si); + duplicate_environment_strings(service->initial_env); + return stop_service(service, exitcode, true, true); + } + service->process_handle = pi.hProcess; + service->pid = pi.dwProcessId; + + if (get_process_creation_time(service->process_handle, &service->creation_time)) ZeroMemory(&service->creation_time, sizeof(service->creation_time)); + + close_output_handles(&si); + + if (! service->no_console) FreeConsole(); + + /* Restore our environment. */ + duplicate_environment_strings(service->initial_env); + + if (service->affinity) { + /* + We are explicitly storing service->affinity as a 64-bit unsigned integer + so that we can parse it regardless of whether we're running in 32-bit + or 64-bit mode. The arguments to SetProcessAffinityMask(), however, are + defined as type DWORD_PTR and hence limited to 32 bits on a 32-bit system + (or when running the 32-bit NSSM). + + The result is a lot of seemingly-unnecessary casting throughout the code + and potentially confusion when we actually try to start the service. + Having said that, however, it's unlikely that we're actually going to + run in 32-bit mode on a system which has more than 32 CPUs so the + likelihood of seeing a confusing situation is somewhat diminished. + */ + DWORD_PTR affinity, system_affinity; + + if (GetProcessAffinityMask(service->process_handle, &affinity, &system_affinity)) affinity = service->affinity & system_affinity; + else { + affinity = (DWORD_PTR) service->affinity; + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0); + } + + if (! SetProcessAffinityMask(service->process_handle, affinity)) { + log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_SETPROCESSAFFINITYMASK_FAILED, service->name, error_string(GetLastError()), 0); + } + + ResumeThread(pi.hThread); + } + + /* + Wait for a clean startup before changing the service status to RUNNING + but be mindful of the fact that we are blocking the service control manager + so abandon the wait before too much time has elapsed. + */ + unsigned long delay = service->throttle_delay; + if (delay > NSSM_SERVICE_STATUS_DEADLINE) { + TCHAR delay_milliseconds[16]; + _sntprintf_s(delay_milliseconds, _countof(delay_milliseconds), _TRUNCATE, _T("%lu"), delay); + TCHAR deadline_milliseconds[16]; + _sntprintf_s(deadline_milliseconds, _countof(deadline_milliseconds), _TRUNCATE, _T("%lu"), NSSM_SERVICE_STATUS_DEADLINE); + log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_STARTUP_DELAY_TOO_LONG, service->name, delay_milliseconds, NSSM, deadline_milliseconds, 0); + delay = NSSM_SERVICE_STATUS_DEADLINE; + } + unsigned long deadline = WaitForSingleObject(service->process_handle, delay); + + /* Signal successful start */ + service->status.dwCurrentState = SERVICE_RUNNING; + SetServiceStatus(service->status_handle, &service->status); + + /* Continue waiting for a clean startup. */ + if (deadline == WAIT_TIMEOUT) { + if (service->throttle_delay > delay) { + if (WaitForSingleObject(service->process_handle, service->throttle_delay - delay) == WAIT_TIMEOUT) service->throttle = 0; + } + else service->throttle = 0; + } + + /* Ensure the restart delay is always applied. */ + if (service->restart_delay && ! service->throttle) service->throttle++; + + return 0; +} + +/* Stop the service */ +int stop_service(nssm_service_t *service, unsigned long exitcode, bool graceful, bool default_action) { + service->allow_restart = false; + if (service->wait_handle) { + UnregisterWait(service->wait_handle); + service->wait_handle = 0; + } + + service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE; + + if (default_action && ! exitcode && ! graceful) { + log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_GRACEFUL_SUICIDE, service->name, service->exe, exit_action_strings[NSSM_EXIT_UNCLEAN], exit_action_strings[NSSM_EXIT_UNCLEAN], exit_action_strings[NSSM_EXIT_UNCLEAN], exit_action_strings[NSSM_EXIT_REALLY], 0); + graceful = true; + } + + /* Signal we are stopping */ + if (graceful) { + service->status.dwCurrentState = SERVICE_STOP_PENDING; + service->status.dwWaitHint = NSSM_WAITHINT_MARGIN; + SetServiceStatus(service->status_handle, &service->status); + } + + /* Nothing to do if service isn't running */ + if (service->pid) { + /* Shut down service */ + log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service->name, service->exe, 0); + kill_process(service, service->process_handle, service->pid, 0); + } + else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service->name, service->exe, 0); + + end_service((void *) service, true); + + /* Signal we stopped */ + if (graceful) { + service->status.dwCurrentState = SERVICE_STOPPED; + if (exitcode) { + service->status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR; + service->status.dwServiceSpecificExitCode = exitcode; + } + else { + service->status.dwWin32ExitCode = NO_ERROR; + service->status.dwServiceSpecificExitCode = 0; + } + SetServiceStatus(service->status_handle, &service->status); + } + + return exitcode; +} + +/* Callback function triggered when the server exits */ +void CALLBACK end_service(void *arg, unsigned char why) { + nssm_service_t *service = (nssm_service_t *) arg; + + if (service->stopping) return; + + service->stopping = true; + + service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_OFFLINE; + + /* Use now as a dummy exit time. */ + GetSystemTimeAsFileTime(&service->exit_time); + + /* Check exit code */ + unsigned long exitcode = 0; + TCHAR code[16]; + if (service->process_handle) { + GetExitCodeProcess(service->process_handle, &exitcode); + /* Check real exit time. */ + if (exitcode != STILL_ACTIVE) get_process_exit_time(service->process_handle, &service->exit_time); + CloseHandle(service->process_handle); + } + + service->process_handle = 0; + + /* + Log that the service ended BEFORE logging about killing the process + tree. See below for the possible values of the why argument. + */ + if (! why) { + _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), exitcode); + log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, service->exe, service->name, code, 0); + } + + /* Clean up. */ + if (exitcode == STILL_ACTIVE) exitcode = 0; + if (service->pid) kill_process_tree(service, service->pid, exitcode, service->pid); + service->pid = 0; + + /* + The why argument is true if our wait timed out or false otherwise. + Our wait is infinite so why will never be true when called by the system. + If it is indeed true, assume we were called from stop_service() because + this is a controlled shutdown, and don't take any restart action. + */ + if (why) return; + if (! service->allow_restart) return; + + /* What action should we take? */ + int action = NSSM_EXIT_RESTART; + TCHAR action_string[ACTION_LEN]; + bool default_action; + if (! get_exit_action(service->name, &exitcode, 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)) { + action = i; + break; + } + } + } + + switch (action) { + /* Try to restart the service or return failure code to service manager */ + case NSSM_EXIT_RESTART: + log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service->name, code, exit_action_strings[action], service->exe, 0); + while (monitor_service(service)) { + log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, service->exe, service->name, 0); + Sleep(30000); + } + break; + + /* Do nothing, just like srvany would */ + case NSSM_EXIT_IGNORE: + log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service->name, code, exit_action_strings[action], service->exe, 0); + Sleep(INFINITE); + break; + + /* Tell the service manager we are finished */ + case NSSM_EXIT_REALLY: + log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service->name, code, exit_action_strings[action], 0); + stop_service(service, exitcode, true, default_action); + break; + + /* Fake a crash so pre-Vista service managers will run recovery actions. */ + case NSSM_EXIT_UNCLEAN: + log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service->name, code, exit_action_strings[action], 0); + stop_service(service, exitcode, false, default_action); + free_imports(); + exit(exitcode); + break; + } +} + +void throttle_restart(nssm_service_t *service) { + /* This can't be a restart if the service is already running. */ + if (! service->throttle++) return; + + unsigned long ms; + unsigned long throttle_ms = throttle_milliseconds(service->throttle); + TCHAR threshold[8], milliseconds[8]; + + if (service->restart_delay > throttle_ms) ms = service->restart_delay; + else ms = throttle_ms; + + if (service->throttle > 7) service->throttle = 8; + + _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), ms); + + if (service->throttle == 1 && service->restart_delay > throttle_ms) log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESTART_DELAY, service->name, milliseconds, 0); + else { + _sntprintf_s(threshold, _countof(threshold), _TRUNCATE, _T("%lu"), service->throttle_delay); + log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service->name, threshold, milliseconds, 0); + } + + if (use_critical_section) EnterCriticalSection(&service->throttle_section); + else if (service->throttle_timer) { + ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime)); + service->throttle_duetime.QuadPart = 0 - (ms * 10000LL); + SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0); + } + + service->status.dwCurrentState = SERVICE_PAUSED; + SetServiceStatus(service->status_handle, &service->status); + + if (use_critical_section) { + imports.SleepConditionVariableCS(&service->throttle_condition, &service->throttle_section, ms); + LeaveCriticalSection(&service->throttle_section); + } + else { + if (service->throttle_timer) WaitForSingleObject(service->throttle_timer, INFINITE); + else Sleep(ms); + } +} + +/* + When responding to a stop (or any other) request we need to set dwWaitHint to + the number of milliseconds we expect the operation to take, and optionally + increase dwCheckPoint. If dwWaitHint milliseconds elapses without the + operation completing or dwCheckPoint increasing, the system will consider the + service to be hung. + + However the system will consider the service to be hung after 30000 + milliseconds regardless of the value of dwWaitHint if dwCheckPoint has not + changed. Therefore if we want to wait longer than that we must periodically + increase dwCheckPoint. + + Furthermore, it will consider the service to be hung after 60000 milliseconds + regardless of the value of dwCheckPoint unless dwWaitHint is increased every + time dwCheckPoint is also increased. + + Our strategy then is to retrieve the initial dwWaitHint and wait for + NSSM_SERVICE_STATUS_DEADLINE milliseconds. If the process is still running + and we haven't finished waiting we increment dwCheckPoint and add whichever is + smaller of NSSM_SERVICE_STATUS_DEADLINE or the remaining timeout to + dwWaitHint. + + Only doing both these things will prevent the system from killing the service. + + Returns: 1 if the wait timed out. + 0 if the wait completed. + -1 on error. +*/ +int await_shutdown(nssm_service_t *service, TCHAR *function_name, unsigned long timeout) { + unsigned long interval; + unsigned long waithint; + unsigned long ret; + unsigned long waited; + TCHAR interval_milliseconds[16]; + TCHAR timeout_milliseconds[16]; + TCHAR waited_milliseconds[16]; + TCHAR *function = function_name; + + /* Add brackets to function name. */ + size_t funclen = _tcslen(function_name) + 3; + TCHAR *func = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, funclen * sizeof(TCHAR)); + if (func) { + if (_sntprintf_s(func, funclen, _TRUNCATE, _T("%s()"), function_name) > -1) function = func; + } + + _sntprintf_s(timeout_milliseconds, _countof(timeout_milliseconds), _TRUNCATE, _T("%lu"), timeout); + + waithint = service->status.dwWaitHint; + waited = 0; + while (waited < timeout) { + interval = timeout - waited; + if (interval > NSSM_SERVICE_STATUS_DEADLINE) interval = NSSM_SERVICE_STATUS_DEADLINE; + + service->status.dwCurrentState = SERVICE_STOP_PENDING; + service->status.dwWaitHint += interval; + service->status.dwCheckPoint++; + SetServiceStatus(service->status_handle, &service->status); + + if (waited) { + _sntprintf_s(waited_milliseconds, _countof(waited_milliseconds), _TRUNCATE, _T("%lu"), waited); + _sntprintf_s(interval_milliseconds, _countof(interval_milliseconds), _TRUNCATE, _T("%lu"), interval); + log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_AWAITING_SHUTDOWN, function, service->name, waited_milliseconds, interval_milliseconds, timeout_milliseconds, 0); + } + + switch (WaitForSingleObject(service->process_handle, interval)) { + case WAIT_OBJECT_0: + ret = 0; + goto awaited; + + case WAIT_TIMEOUT: + ret = 1; + break; + + default: + ret = -1; + goto awaited; + } + + waited += interval; + } + +awaited: + if (func) HeapFree(GetProcessHeap(), 0, func); + + return ret; +} diff --git a/Static/influxDB/nssm-2.24/src/service.h b/Static/influxDB/nssm-2.24/src/service.h new file mode 100644 index 0000000..4c9ba35 --- /dev/null +++ b/Static/influxDB/nssm-2.24/src/service.h @@ -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 diff --git a/Static/influxDB/nssm-2.24/src/settings.cpp b/Static/influxDB/nssm-2.24/src/settings.cpp new file mode 100644 index 0000000..14b279a --- /dev/null +++ b/Static/influxDB/nssm-2.24/src/settings.cpp @@ -0,0 +1,1061 @@ +#include "nssm.h" +/* XXX: (value && value->string) is probably bogus because value is probably never null */ + +/* Affinity. */ +#define NSSM_AFFINITY_ALL _T("All") + +extern const TCHAR *exit_action_strings[]; +extern const TCHAR *startup_strings[]; +extern const TCHAR *priority_strings[]; + +/* Does the parameter refer to the default value of the setting? */ +static inline int is_default(const TCHAR *value) { + return (str_equiv(value, _T("default")) || str_equiv(value, _T("*")) || ! value[0]); +} + +static int value_from_string(const TCHAR *name, value_t *value, const TCHAR *string) { + size_t len = _tcslen(string); + if (! len++) { + value->string = 0; + return 0; + } + + value->string = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR)); + if (! value->string) { + print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, name, _T("value_from_string()")); + return -1; + } + + if (_sntprintf_s(value->string, len, _TRUNCATE, _T("%s"), string) < 0) { + HeapFree(GetProcessHeap(), 0, value->string); + print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, name, _T("value_from_string()")); + return -1; + } + + return 1; +} + +/* Functions to manage NSSM-specific settings in the registry. */ +static int setting_set_number(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) { + HKEY key = (HKEY) param; + if (! key) return -1; + + unsigned long number; + long error; + + /* Resetting to default? */ + if (! value || ! value->string) { + error = RegDeleteValue(key, name); + if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0; + print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error)); + return -1; + } + if (str_number(value->string, &number)) return -1; + + if (default_value && number == (unsigned long) default_value) { + error = RegDeleteValue(key, name); + if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0; + print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error)); + return -1; + } + + if (set_number(key, (TCHAR *) name, number)) return -1; + + return 1; +} + +static int setting_get_number(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) { + HKEY key = (HKEY) param; + return get_number(key, (TCHAR *) name, &value->numeric, false); +} + +static int setting_set_string(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) { + HKEY key = (HKEY) param; + if (! key) return -1; + + long error; + + /* Resetting to default? */ + if (! value || ! value->string) { + if (default_value) value->string = (TCHAR *) default_value; + else { + error = RegDeleteValue(key, name); + if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0; + print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error)); + return -1; + } + } + if (default_value && _tcslen((TCHAR *) default_value) && str_equiv(value->string, (TCHAR *) default_value)) { + error = RegDeleteValue(key, name); + if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0; + print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error)); + return -1; + } + + if (set_expand_string(key, (TCHAR *) name, value->string)) return -1; + + return 1; +} + +static int setting_get_string(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) { + HKEY key = (HKEY) param; + TCHAR buffer[VALUE_LENGTH]; + + if (get_string(key, (TCHAR *) name, (TCHAR *) buffer, (unsigned long) sizeof(buffer), false, false, false)) return -1; + + return value_from_string(name, value, buffer); +} + +static int setting_set_exit_action(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) { + unsigned long exitcode; + TCHAR *code; + TCHAR action_string[ACTION_LEN]; + + if (additional) { + /* Default action? */ + if (is_default(additional)) code = 0; + else { + if (str_number(additional, &exitcode)) return -1; + code = (TCHAR *) additional; + } + } + + HKEY key = open_registry(service_name, name, KEY_WRITE); + if (! key) return -1; + + long error; + int ret = 1; + + /* Resetting to default? */ + if (value && value->string) _sntprintf_s(action_string, _countof(action_string), _TRUNCATE, _T("%s"), value->string); + else { + if (code) { + /* Delete explicit action. */ + error = RegDeleteValue(key, code); + RegCloseKey(key); + if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0; + print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, code, service_name, error_string(error)); + return -1; + } + else { + /* Explicitly keep the default action. */ + if (default_value) _sntprintf_s(action_string, _countof(action_string), _TRUNCATE, _T("%s"), (TCHAR *) default_value); + ret = 0; + } + } + + /* Validate the string. */ + for (int i = 0; exit_action_strings[i]; i++) { + if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) { + if (default_value && str_equiv(action_string, (TCHAR *) default_value)) ret = 0; + if (RegSetValueEx(key, code, 0, REG_SZ, (const unsigned char *) exit_action_strings[i], (unsigned long) (_tcslen(action_string) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) { + print_message(stderr, NSSM_MESSAGE_SETVALUE_FAILED, code, service_name, error_string(GetLastError())); + RegCloseKey(key); + return -1; + } + + RegCloseKey(key); + return ret; + } + } + + print_message(stderr, NSSM_MESSAGE_INVALID_EXIT_ACTION, action_string); + for (int i = 0; exit_action_strings[i]; i++) _ftprintf(stderr, _T("%s\n"), exit_action_strings[i]); + + return -1; +} + +static int setting_get_exit_action(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) { + unsigned long exitcode = 0; + unsigned long *code = 0; + + if (additional) { + if (! is_default(additional)) { + if (str_number(additional, &exitcode)) return -1; + code = &exitcode; + } + } + + TCHAR action_string[ACTION_LEN]; + bool default_action; + if (get_exit_action(service_name, code, action_string, &default_action)) return -1; + + value_from_string(name, value, action_string); + + if (default_action && ! _tcsnicmp((const TCHAR *) action_string, (TCHAR *) default_value, ACTION_LEN)) return 0; + return 1; +} + +static int setting_set_affinity(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) { + HKEY key = (HKEY) param; + if (! key) return -1; + + long error; + __int64 mask; + __int64 system_affinity = 0LL; + + if (value && value->string) { + DWORD_PTR affinity; + if (! GetProcessAffinityMask(GetCurrentProcess(), &affinity, (DWORD_PTR *) &system_affinity)) system_affinity = ~0; + + if (is_default(value->string) || str_equiv(value->string, NSSM_AFFINITY_ALL)) mask = 0LL; + else if (affinity_string_to_mask(value->string, &mask)) { + print_message(stderr, NSSM_MESSAGE_BOGUS_AFFINITY_MASK, value->string, num_cpus() - 1); + return -1; + } + } + else mask = 0LL; + + if (! mask) { + error = RegDeleteValue(key, name); + if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0; + print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error)); + return -1; + } + + /* Canonicalise. */ + TCHAR *canon = 0; + if (affinity_mask_to_string(mask, &canon)) canon = value->string; + + __int64 effective_affinity = mask & system_affinity; + if (effective_affinity != mask) { + /* Requested CPUs did not intersect with available CPUs? */ + if (! effective_affinity) mask = effective_affinity = system_affinity; + + TCHAR *system = 0; + if (! affinity_mask_to_string(system_affinity, &system)) { + TCHAR *effective = 0; + if (! affinity_mask_to_string(effective_affinity, &effective)) { + print_message(stderr, NSSM_MESSAGE_EFFECTIVE_AFFINITY_MASK, value->string, system, effective); + HeapFree(GetProcessHeap(), 0, effective); + } + HeapFree(GetProcessHeap(), 0, system); + } + } + + if (RegSetValueEx(key, name, 0, REG_SZ, (const unsigned char *) canon, (unsigned long) (_tcslen(canon) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) { + if (canon != value->string) HeapFree(GetProcessHeap(), 0, canon); + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, name, error_string(GetLastError()), 0); + return -1; + } + + if (canon != value->string) HeapFree(GetProcessHeap(), 0, canon); + return 1; +} + +static int setting_get_affinity(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) { + HKEY key = (HKEY) param; + if (! key) return -1; + + unsigned long type; + TCHAR *buffer = 0; + unsigned long buflen = 0; + + int ret = RegQueryValueEx(key, name, 0, &type, 0, &buflen); + if (ret == ERROR_FILE_NOT_FOUND) { + if (value_from_string(name, value, NSSM_AFFINITY_ALL) == 1) return 0; + return -1; + } + if (ret != ERROR_SUCCESS) return -1; + + if (type != REG_SZ) return -1; + + buffer = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, buflen); + if (! buffer) { + print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("affinity"), _T("setting_get_affinity")); + return -1; + } + + if (get_string(key, (TCHAR *) name, buffer, buflen, false, false, true)) { + HeapFree(GetProcessHeap(), 0, buffer); + return -1; + } + + __int64 affinity; + if (affinity_string_to_mask(buffer, &affinity)) { + print_message(stderr, NSSM_MESSAGE_BOGUS_AFFINITY_MASK, buffer, num_cpus() - 1); + HeapFree(GetProcessHeap(), 0, buffer); + return -1; + } + + HeapFree(GetProcessHeap(), 0, buffer); + + /* Canonicalise. */ + if (affinity_mask_to_string(affinity, &buffer)) { + if (buffer) HeapFree(GetProcessHeap(), 0, buffer); + return -1; + } + + ret = value_from_string(name, value, buffer); + HeapFree(GetProcessHeap(), 0, buffer); + return ret; +} + +static int setting_set_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) { + HKEY key = (HKEY) param; + if (! param) return -1; + + if (! value || ! value->string || ! value->string[0]) { + long error = RegDeleteValue(key, name); + if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0; + print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error)); + return -1; + } + + unsigned long envlen = (unsigned long) _tcslen(value->string) + 1; + TCHAR *unformatted = 0; + unsigned long newlen; + if (unformat_double_null(value->string, envlen, &unformatted, &newlen)) return -1; + + if (test_environment(unformatted)) { + HeapFree(GetProcessHeap(), 0, unformatted); + print_message(stderr, NSSM_GUI_INVALID_ENVIRONMENT); + return -1; + } + + if (RegSetValueEx(key, name, 0, REG_MULTI_SZ, (const unsigned char *) unformatted, (unsigned long) newlen * sizeof(TCHAR)) != ERROR_SUCCESS) { + if (newlen) HeapFree(GetProcessHeap(), 0, unformatted); + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV, error_string(GetLastError()), 0); + return -1; + } + + if (newlen) HeapFree(GetProcessHeap(), 0, unformatted); + return 1; +} + +static int setting_get_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) { + HKEY key = (HKEY) param; + if (! param) return -1; + + TCHAR *env = 0; + unsigned long envlen; + if (get_environment((TCHAR *) service_name, key, (TCHAR *) name, &env, &envlen)) return -1; + if (! envlen) return 0; + + TCHAR *formatted; + unsigned long newlen; + if (format_double_null(env, envlen, &formatted, &newlen)) return -1; + + int ret; + if (additional) { + /* Find named environment variable. */ + TCHAR *s; + size_t len = _tcslen(additional); + for (s = env; *s; s++) { + /* Look for = NULL NULL */ + if (! _tcsnicmp(s, additional, len) && s[len] == _T('=')) { + /* Strip = */ + s += len + 1; + ret = value_from_string(name, value, s); + HeapFree(GetProcessHeap(), 0, env); + return ret; + } + + /* Skip this string. */ + for ( ; *s; s++); + } + HeapFree(GetProcessHeap(), 0, env); + return 0; + } + + HeapFree(GetProcessHeap(), 0, env); + + ret = value_from_string(name, value, formatted); + if (newlen) HeapFree(GetProcessHeap(), 0, formatted); + return ret; +} + +static int setting_set_priority(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) { + HKEY key = (HKEY) param; + if (! param) return -1; + + TCHAR *priority_string; + int i; + long error; + + if (value && value->string) priority_string = value->string; + else if (default_value) priority_string = (TCHAR *) default_value; + else { + error = RegDeleteValue(key, name); + if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0; + print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error)); + return -1; + } + + for (i = 0; priority_strings[i]; i++) { + if (! str_equiv(priority_strings[i], priority_string)) continue; + + if (default_value && str_equiv(priority_string, (TCHAR *) default_value)) { + error = RegDeleteValue(key, name); + if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0; + print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error)); + return -1; + } + + if (set_number(key, (TCHAR *) name, priority_index_to_constant(i))) return -1; + return 1; + } + + print_message(stderr, NSSM_MESSAGE_INVALID_PRIORITY, priority_string); + for (i = 0; priority_strings[i]; i++) _ftprintf(stderr, _T("%s\n"), priority_strings[i]); + + return -1; +} + +static int setting_get_priority(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) { + HKEY key = (HKEY) param; + if (! param) return -1; + + unsigned long constant; + switch (get_number(key, (TCHAR *) name, &constant, false)) { + case 0: return value_from_string(name, value, (const TCHAR *) default_value); + case -1: return -1; + } + + return value_from_string(name, value, priority_strings[priority_constant_to_index(constant)]); +} + +/* Functions to manage native service settings. */ +static int native_set_dependongroup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) { + SC_HANDLE service_handle = (SC_HANDLE) param; + if (! service_handle) return -1; + + /* + Get existing service dependencies because we must set both types together. + */ + TCHAR *buffer; + unsigned long buflen; + if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, DEPENDENCY_SERVICES)) return -1; + + if (! value || ! value->string || ! value->string[0]) { + if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, buffer, 0, 0, 0)) { + print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError())); + if (buffer) HeapFree(GetProcessHeap(), 0, buffer); + return -1; + } + + if (buffer) HeapFree(GetProcessHeap(), 0, buffer); + return 0; + } + + unsigned long len = (unsigned long) _tcslen(value->string) + 1; + TCHAR *unformatted = 0; + unsigned long newlen; + if (unformat_double_null(value->string, len, &unformatted, &newlen)) { + if (buffer) HeapFree(GetProcessHeap(), 0, buffer); + return -1; + } + + /* Prepend group identifier. */ + unsigned long missing = 0; + TCHAR *canon = unformatted; + size_t canonlen = 0; + TCHAR *s; + for (s = unformatted; *s; s++) { + if (*s != SC_GROUP_IDENTIFIER) missing++; + size_t len = _tcslen(s); + canonlen += len + 1; + s += len; + } + + if (missing) { + /* Missing identifiers plus double NULL terminator. */ + canonlen += missing + 1; + newlen = (unsigned long) canonlen; + + canon = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, canonlen * sizeof(TCHAR)); + if (! canon) { + print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canon"), _T("native_set_dependongroup")); + if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted); + if (buffer) HeapFree(GetProcessHeap(), 0, buffer); + return -1; + } + + size_t i = 0; + for (s = unformatted; *s; s++) { + if (*s != SC_GROUP_IDENTIFIER) canon[i++] = SC_GROUP_IDENTIFIER; + size_t len = _tcslen(s); + memmove(canon + i, s, (len + 1) * sizeof(TCHAR)); + i += len + 1; + s += len; + } + } + + TCHAR *dependencies; + if (buflen > 2) { + dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (newlen + buflen) * sizeof(TCHAR)); + if (! dependencies) { + print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("dependencies"), _T("native_set_dependongroup")); + if (canon != unformatted) HeapFree(GetProcessHeap(), 0, canon); + if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted); + if (buffer) HeapFree(GetProcessHeap(), 0, buffer); + return -1; + } + + memmove(dependencies, buffer, buflen * sizeof(TCHAR)); + memmove(dependencies + buflen - 1, canon, newlen * sizeof(TCHAR)); + } + else dependencies = canon; + + int ret = 1; + if (set_service_dependencies(service_name, service_handle, dependencies)) ret = -1; + if (dependencies != unformatted) HeapFree(GetProcessHeap(), 0, dependencies); + if (canon != unformatted) HeapFree(GetProcessHeap(), 0, canon); + if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted); + if (buffer) HeapFree(GetProcessHeap(), 0, buffer); + + return ret; +} + +static int native_get_dependongroup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) { + SC_HANDLE service_handle = (SC_HANDLE) param; + if (! service_handle) return -1; + + TCHAR *buffer; + unsigned long buflen; + if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, DEPENDENCY_GROUPS)) return -1; + + int ret; + if (buflen) { + TCHAR *formatted; + unsigned long newlen; + if (format_double_null(buffer, buflen, &formatted, &newlen)) { + HeapFree(GetProcessHeap(), 0, buffer); + return -1; + } + + ret = value_from_string(name, value, formatted); + HeapFree(GetProcessHeap(), 0, formatted); + HeapFree(GetProcessHeap(), 0, buffer); + } + else { + value->string = 0; + ret = 0; + } + + return ret; +} + +static int native_set_dependonservice(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) { + SC_HANDLE service_handle = (SC_HANDLE) param; + if (! service_handle) return -1; + + /* + Get existing group dependencies because we must set both types together. + */ + TCHAR *buffer; + unsigned long buflen; + if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, DEPENDENCY_GROUPS)) return -1; + + if (! value || ! value->string || ! value->string[0]) { + if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, buffer, 0, 0, 0)) { + print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError())); + if (buffer) HeapFree(GetProcessHeap(), 0, buffer); + return -1; + } + + if (buffer) HeapFree(GetProcessHeap(), 0, buffer); + return 0; + } + + unsigned long len = (unsigned long) _tcslen(value->string) + 1; + TCHAR *unformatted = 0; + unsigned long newlen; + if (unformat_double_null(value->string, len, &unformatted, &newlen)) { + if (buffer) HeapFree(GetProcessHeap(), 0, buffer); + return -1; + } + + TCHAR *dependencies; + if (buflen > 2) { + dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (newlen + buflen) * sizeof(TCHAR)); + if (! dependencies) { + print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("dependencies"), _T("native_set_dependonservice")); + if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted); + if (buffer) HeapFree(GetProcessHeap(), 0, buffer); + return -1; + } + + memmove(dependencies, buffer, buflen * sizeof(TCHAR)); + memmove(dependencies + buflen - 1, unformatted, newlen * sizeof(TCHAR)); + } + else dependencies = unformatted; + + int ret = 1; + if (set_service_dependencies(service_name, service_handle, dependencies)) ret = -1; + if (dependencies != unformatted) HeapFree(GetProcessHeap(), 0, dependencies); + if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted); + if (buffer) HeapFree(GetProcessHeap(), 0, buffer); + + return ret; +} + +static int native_get_dependonservice(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) { + SC_HANDLE service_handle = (SC_HANDLE) param; + if (! service_handle) return -1; + + TCHAR *buffer; + unsigned long buflen; + if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, DEPENDENCY_SERVICES)) return -1; + + int ret; + if (buflen) { + TCHAR *formatted; + unsigned long newlen; + if (format_double_null(buffer, buflen, &formatted, &newlen)) { + HeapFree(GetProcessHeap(), 0, buffer); + return -1; + } + + ret = value_from_string(name, value, formatted); + HeapFree(GetProcessHeap(), 0, formatted); + HeapFree(GetProcessHeap(), 0, buffer); + } + else { + value->string = 0; + ret = 0; + } + + return ret; +} + +int native_set_description(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) { + SC_HANDLE service_handle = (SC_HANDLE) param; + if (! service_handle) return -1; + + TCHAR *description = 0; + if (value) description = value->string; + if (set_service_description(service_name, service_handle, description)) return -1; + + if (description && description[0]) return 1; + + return 0; +} + +int native_get_description(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) { + SC_HANDLE service_handle = (SC_HANDLE) param; + if (! service_handle) return -1; + + TCHAR buffer[VALUE_LENGTH]; + if (get_service_description(service_name, service_handle, _countof(buffer), buffer)) return -1; + + if (buffer[0]) return value_from_string(name, value, buffer); + value->string = 0; + + return 0; +} + +int native_set_displayname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) { + SC_HANDLE service_handle = (SC_HANDLE) param; + if (! service_handle) return -1; + + TCHAR *displayname = 0; + if (value && value->string) displayname = value->string; + else displayname = (TCHAR *) service_name; + + if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, displayname)) { + print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError())); + return -1; + } + + /* + If the display name and service name differ only in case, + ChangeServiceConfig() will return success but the display name will be + set to the service name, NOT the value passed to the function. + This appears to be a quirk of Windows rather than a bug here. + */ + if (displayname != service_name && ! str_equiv(displayname, service_name)) return 1; + + return 0; +} + +int native_get_displayname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) { + SC_HANDLE service_handle = (SC_HANDLE) param; + if (! service_handle) return -1; + + QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle); + if (! qsc) return -1; + + int ret = value_from_string(name, value, qsc->lpDisplayName); + HeapFree(GetProcessHeap(), 0, qsc); + + return ret; +} + +int native_set_imagepath(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) { + SC_HANDLE service_handle = (SC_HANDLE) param; + if (! service_handle) return -1; + + /* It makes no sense to try to reset the image path. */ + if (! value || ! value->string) { + print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name); + return -1; + } + + if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, value->string, 0, 0, 0, 0, 0, 0)) { + print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError())); + return -1; + } + + return 1; +} + +int native_get_imagepath(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) { + SC_HANDLE service_handle = (SC_HANDLE) param; + if (! service_handle) return -1; + + QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle); + if (! qsc) return -1; + + int ret = value_from_string(name, value, qsc->lpBinaryPathName); + HeapFree(GetProcessHeap(), 0, qsc); + + return ret; +} + +int native_set_name(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) { + print_message(stderr, NSSM_MESSAGE_CANNOT_RENAME_SERVICE); + return -1; +} + +int native_get_name(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) { + return value_from_string(name, value, service_name); +} + +int native_set_objectname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) { + SC_HANDLE service_handle = (SC_HANDLE) param; + if (! service_handle) return -1; + + /* + Logical syntax is: nssm set ObjectName + That means the username is actually passed in the additional parameter. + */ + bool localsystem = false; + TCHAR *username = NSSM_LOCALSYSTEM_ACCOUNT; + TCHAR *password = 0; + if (additional) { + username = (TCHAR *) additional; + if (value && value->string) password = value->string; + } + else if (value && value->string) username = value->string; + + const TCHAR *well_known = well_known_username(username); + size_t passwordsize = 0; + if (well_known) { + if (str_equiv(well_known, NSSM_LOCALSYSTEM_ACCOUNT)) localsystem = true; + username = (TCHAR *) well_known; + password = _T(""); + } + else if (! password) { + /* We need a password if the account requires it. */ + print_message(stderr, NSSM_MESSAGE_MISSING_PASSWORD, name); + return -1; + } + else passwordsize = _tcslen(password) * sizeof(TCHAR); + + /* + ChangeServiceConfig() will fail to set the username if the service is set + to interact with the desktop. + */ + unsigned long type = SERVICE_NO_CHANGE; + if (! localsystem) { + QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle); + if (! qsc) { + if (passwordsize) SecureZeroMemory(password, passwordsize); + return -1; + } + + type = qsc->dwServiceType & ~SERVICE_INTERACTIVE_PROCESS; + HeapFree(GetProcessHeap(), 0, qsc); + } + + if (! well_known) { + if (grant_logon_as_service(username)) { + if (passwordsize) SecureZeroMemory(password, passwordsize); + print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username); + return -1; + } + } + + if (! ChangeServiceConfig(service_handle, type, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, username, password, 0)) { + if (passwordsize) SecureZeroMemory(password, passwordsize); + print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError())); + return -1; + } + + if (passwordsize) SecureZeroMemory(password, passwordsize); + + if (localsystem) return 0; + + return 1; +} + +int native_get_objectname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) { + SC_HANDLE service_handle = (SC_HANDLE) param; + if (! service_handle) return -1; + + QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle); + if (! qsc) return -1; + + int ret = value_from_string(name, value, qsc->lpServiceStartName); + HeapFree(GetProcessHeap(), 0, qsc); + + return ret; +} + +int native_set_startup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) { + SC_HANDLE service_handle = (SC_HANDLE) param; + if (! service_handle) return -1; + + /* It makes no sense to try to reset the startup type. */ + if (! value || ! value->string) { + print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name); + return -1; + } + + /* Map NSSM_STARTUP_* constant to Windows SERVICE_*_START constant. */ + int service_startup = -1; + int i; + for (i = 0; startup_strings[i]; i++) { + if (str_equiv(value->string, startup_strings[i])) { + service_startup = i; + break; + } + } + + if (service_startup < 0) { + print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE_STARTUP, value->string); + for (i = 0; startup_strings[i]; i++) _ftprintf(stderr, _T("%s\n"), startup_strings[i]); + return -1; + } + + unsigned long startup; + switch (service_startup) { + case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break; + case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break; + default: startup = SERVICE_AUTO_START; + } + + if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, startup, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0)) { + print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError())); + return -1; + } + + SERVICE_DELAYED_AUTO_START_INFO delayed; + ZeroMemory(&delayed, sizeof(delayed)); + if (service_startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1; + else delayed.fDelayedAutostart = 0; + if (! ChangeServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) { + unsigned long error = GetLastError(); + /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */ + if (error != ERROR_INVALID_LEVEL) { + log_event(EVENTLOG_ERROR_TYPE, NSSM_MESSAGE_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service_name, error_string(error), 0); + } + } + + return 1; +} + +int native_get_startup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) { + SC_HANDLE service_handle = (SC_HANDLE) param; + if (! service_handle) return -1; + + QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle); + if (! qsc) return -1; + + unsigned long startup; + int ret = get_service_startup(service_name, service_handle, qsc, &startup); + HeapFree(GetProcessHeap(), 0, qsc); + + if (ret) return -1; + + unsigned long i; + for (i = 0; startup_strings[i]; i++); + if (startup >= i) return -1; + + return value_from_string(name, value, startup_strings[startup]); +} + +int native_set_type(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) { + SC_HANDLE service_handle = (SC_HANDLE) param; + if (! service_handle) return -1; + + /* It makes no sense to try to reset the service type. */ + if (! value || ! value->string) { + print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name); + return -1; + } + + /* + We can only manage services of type SERVICE_WIN32_OWN_PROCESS + and SERVICE_INTERACTIVE_PROCESS. + */ + unsigned long type = SERVICE_WIN32_OWN_PROCESS; + if (str_equiv(value->string, NSSM_INTERACTIVE_PROCESS)) type |= SERVICE_INTERACTIVE_PROCESS; + else if (! str_equiv(value->string, NSSM_WIN32_OWN_PROCESS)) { + print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE_TYPE, value->string); + _ftprintf(stderr, _T("%s\n"), NSSM_WIN32_OWN_PROCESS); + _ftprintf(stderr, _T("%s\n"), NSSM_INTERACTIVE_PROCESS); + return -1; + } + + /* + ChangeServiceConfig() will fail if the service runs under an account + other than LOCALSYSTEM and we try to make it interactive. + */ + if (type & SERVICE_INTERACTIVE_PROCESS) { + QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle); + if (! qsc) return -1; + + if (! str_equiv(qsc->lpServiceStartName, NSSM_LOCALSYSTEM_ACCOUNT)) { + HeapFree(GetProcessHeap(), 0, qsc); + print_message(stderr, NSSM_MESSAGE_INTERACTIVE_NOT_LOCALSYSTEM, value->string, service_name, NSSM_LOCALSYSTEM_ACCOUNT); + return -1; + } + + HeapFree(GetProcessHeap(), 0, qsc); + } + + if (! ChangeServiceConfig(service_handle, type, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0)) { + print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError())); + return -1; + } + + return 1; +} + +int native_get_type(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) { + SC_HANDLE service_handle = (SC_HANDLE) param; + if (! service_handle) return -1; + + QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle); + if (! qsc) return -1; + + value->numeric = qsc->dwServiceType; + HeapFree(GetProcessHeap(), 0, qsc); + + const TCHAR *string; + switch (value->numeric) { + case SERVICE_KERNEL_DRIVER: string = NSSM_KERNEL_DRIVER; break; + case SERVICE_FILE_SYSTEM_DRIVER: string = NSSM_FILE_SYSTEM_DRIVER; break; + case SERVICE_WIN32_OWN_PROCESS: string = NSSM_WIN32_OWN_PROCESS; break; + case SERVICE_WIN32_SHARE_PROCESS: string = NSSM_WIN32_SHARE_PROCESS; break; + case SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS: string = NSSM_INTERACTIVE_PROCESS; break; + case SERVICE_WIN32_SHARE_PROCESS|SERVICE_INTERACTIVE_PROCESS: string = NSSM_SHARE_INTERACTIVE_PROCESS; break; + default: string = NSSM_UNKNOWN; + } + + return value_from_string(name, value, string); +} + +int set_setting(const TCHAR *service_name, HKEY key, settings_t *setting, value_t *value, const TCHAR *additional) { + if (! key) return -1; + int ret; + + if (setting->set) ret = setting->set(service_name, (void *) key, setting->name, setting->default_value, value, additional); + else ret = -1; + + if (! ret) print_message(stdout, NSSM_MESSAGE_RESET_SETTING, setting->name, service_name); + else if (ret > 0) print_message(stdout, NSSM_MESSAGE_SET_SETTING, setting->name, service_name); + else print_message(stderr, NSSM_MESSAGE_SET_SETTING_FAILED, setting->name, service_name); + + return ret; +} + +int set_setting(const TCHAR *service_name, SC_HANDLE service_handle, settings_t *setting, value_t *value, const TCHAR *additional) { + if (! service_handle) return -1; + + int ret; + if (setting->set) ret = setting->set(service_name, service_handle, setting->name, setting->default_value, value, additional); + else ret = -1; + + if (! ret) print_message(stdout, NSSM_MESSAGE_RESET_SETTING, setting->name, service_name); + else if (ret > 0) print_message(stdout, NSSM_MESSAGE_SET_SETTING, setting->name, service_name); + else print_message(stderr, NSSM_MESSAGE_SET_SETTING_FAILED, setting->name, service_name); + + return ret; +} + +/* + Returns: 1 if the value was retrieved. + 0 if the default value was retrieved. + -1 on error. +*/ +int get_setting(const TCHAR *service_name, HKEY key, settings_t *setting, value_t *value, const TCHAR *additional) { + if (! key) return -1; + int ret; + + switch (setting->type) { + case REG_EXPAND_SZ: + case REG_MULTI_SZ: + case REG_SZ: + value->string = (TCHAR *) setting->default_value; + if (setting->get) ret = setting->get(service_name, (void *) key, setting->name, setting->default_value, value, additional); + else ret = -1; + break; + + case REG_DWORD: + value->numeric = (unsigned long) setting->default_value; + if (setting->get) ret = setting->get(service_name, (void *) key, setting->name, setting->default_value, value, additional); + else ret = -1; + break; + + default: + ret = -1; + break; + } + + if (ret < 0) print_message(stderr, NSSM_MESSAGE_GET_SETTING_FAILED, setting->name, service_name); + + return ret; +} + +int get_setting(const TCHAR *service_name, SC_HANDLE service_handle, settings_t *setting, value_t *value, const TCHAR *additional) { + if (! service_handle) return -1; + return setting->get(service_name, service_handle, setting->name, 0, value, additional); +} + +settings_t settings[] = { + { NSSM_REG_EXE, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string }, + { NSSM_REG_FLAGS, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string }, + { NSSM_REG_DIR, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string }, + { NSSM_REG_EXIT, REG_SZ, (void *) exit_action_strings[NSSM_EXIT_RESTART], false, ADDITIONAL_MANDATORY, setting_set_exit_action, setting_get_exit_action }, + { NSSM_REG_AFFINITY, REG_SZ, 0, false, 0, setting_set_affinity, setting_get_affinity }, + { NSSM_REG_ENV, REG_MULTI_SZ, NULL, false, ADDITIONAL_CRLF, setting_set_environment, setting_get_environment }, + { NSSM_REG_ENV_EXTRA, REG_MULTI_SZ, NULL, false, ADDITIONAL_CRLF, setting_set_environment, setting_get_environment }, + { NSSM_REG_NO_CONSOLE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number }, + { NSSM_REG_PRIORITY, REG_SZ, (void *) priority_strings[NSSM_NORMAL_PRIORITY], false, 0, setting_set_priority, setting_get_priority }, + { NSSM_REG_RESTART_DELAY, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number }, + { NSSM_REG_STDIN, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string }, + { NSSM_REG_STDIN NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDIN_SHARING, false, 0, setting_set_number, setting_get_number }, + { NSSM_REG_STDIN NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDIN_DISPOSITION, false, 0, setting_set_number, setting_get_number }, + { NSSM_REG_STDIN NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDIN_FLAGS, false, 0, setting_set_number, setting_get_number }, + { NSSM_REG_STDOUT, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string }, + { NSSM_REG_STDOUT NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDOUT_SHARING, false, 0, setting_set_number, setting_get_number }, + { NSSM_REG_STDOUT NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDOUT_DISPOSITION, false, 0, setting_set_number, setting_get_number }, + { NSSM_REG_STDOUT NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDOUT_FLAGS, false, 0, setting_set_number, setting_get_number }, + { NSSM_REG_STDERR, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string }, + { NSSM_REG_STDERR NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDERR_SHARING, false, 0, setting_set_number, setting_get_number }, + { NSSM_REG_STDERR NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDERR_DISPOSITION, false, 0, setting_set_number, setting_get_number }, + { NSSM_REG_STDERR NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDERR_FLAGS, false, 0, setting_set_number, setting_get_number }, + { NSSM_REG_STOP_METHOD_SKIP, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number }, + { NSSM_REG_KILL_CONSOLE_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_CONSOLE_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number }, + { NSSM_REG_KILL_WINDOW_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_WINDOW_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number }, + { NSSM_REG_KILL_THREADS_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_THREADS_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number }, + { NSSM_REG_THROTTLE, REG_DWORD, (void *) NSSM_RESET_THROTTLE_RESTART, false, 0, setting_set_number, setting_get_number }, + { NSSM_REG_ROTATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number }, + { NSSM_REG_ROTATE_ONLINE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number }, + { NSSM_REG_ROTATE_SECONDS, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number }, + { NSSM_REG_ROTATE_BYTES_LOW, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number }, + { NSSM_REG_ROTATE_BYTES_HIGH, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number }, + { NSSM_NATIVE_DEPENDONGROUP, REG_MULTI_SZ, NULL, true, ADDITIONAL_CRLF, native_set_dependongroup, native_get_dependongroup }, + { NSSM_NATIVE_DEPENDONSERVICE, REG_MULTI_SZ, NULL, true, ADDITIONAL_CRLF, native_set_dependonservice, native_get_dependonservice }, + { NSSM_NATIVE_DESCRIPTION, REG_SZ, _T(""), true, 0, native_set_description, native_get_description }, + { NSSM_NATIVE_DISPLAYNAME, REG_SZ, NULL, true, 0, native_set_displayname, native_get_displayname }, + { NSSM_NATIVE_IMAGEPATH, REG_EXPAND_SZ, NULL, true, 0, native_set_imagepath, native_get_imagepath }, + { NSSM_NATIVE_OBJECTNAME, REG_SZ, NSSM_LOCALSYSTEM_ACCOUNT, true, 0, native_set_objectname, native_get_objectname }, + { NSSM_NATIVE_NAME, REG_SZ, NULL, true, 0, native_set_name, native_get_name }, + { NSSM_NATIVE_STARTUP, REG_SZ, NULL, true, 0, native_set_startup, native_get_startup }, + { NSSM_NATIVE_TYPE, REG_SZ, NULL, true, 0, native_set_type, native_get_type }, + { NULL, NULL, NULL, NULL, NULL } +}; diff --git a/Static/influxDB/nssm-2.24/src/settings.h b/Static/influxDB/nssm-2.24/src/settings.h new file mode 100644 index 0000000..a629839 --- /dev/null +++ b/Static/influxDB/nssm-2.24/src/settings.h @@ -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 diff --git a/Static/influxDB/nssm-2.24/src/version.cmd b/Static/influxDB/nssm-2.24/src/version.cmd new file mode 100644 index 0000000..b16e7d4 --- /dev/null +++ b/Static/influxDB/nssm-2.24/src/version.cmd @@ -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) diff --git a/Static/influxDB/nssm-2.24/win32/nssm.exe b/Static/influxDB/nssm-2.24/win32/nssm.exe new file mode 100644 index 0000000..8faee45 Binary files /dev/null and b/Static/influxDB/nssm-2.24/win32/nssm.exe differ diff --git a/Static/influxDB/nssm-2.24/win64/nssm.exe b/Static/influxDB/nssm-2.24/win64/nssm.exe new file mode 100644 index 0000000..6ccfe3c Binary files /dev/null and b/Static/influxDB/nssm-2.24/win64/nssm.exe differ diff --git a/model/HistoryDBModel/HistoryDBManage.py b/model/HistoryDBModel/HistoryDBManage.py index e7382c6..95b191b 100644 --- a/model/HistoryDBModel/HistoryDBManage.py +++ b/model/HistoryDBModel/HistoryDBManage.py @@ -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)