Command to dump service configuration.
authorIain Patterson <me@iain.cx>
Fri, 15 Jul 2016 14:09:58 +0000 (15:09 +0100)
committerIain Patterson <me@iain.cx>
Thu, 28 Jul 2016 15:44:30 +0000 (16:44 +0100)
Output commands which can be used to recreate the service configuration
with:

    nssm dump <servicename>

Commands are quoted and escaped as best we can manage.

README.txt
nssm.cpp
service.cpp
settings.cpp
settings.h

index 6f9004e..d771faa 100644 (file)
@@ -70,6 +70,8 @@ Since version 2.25, NSSM can execute commands in response to service events.
 \r
 Since version 2.25, NSSM can list services it manages.\r
 \r
 \r
 Since version 2.25, NSSM can list services it manages.\r
 \r
+Since version 2.25, NSSM can dump the configuration of services it manages.\r
+\r
 \r
 Usage\r
 -----\r
 \r
 Usage\r
 -----\r
@@ -867,6 +869,20 @@ The following command will print the names of all services managed by NSSM:
     nssm list\r
 \r
 \r
     nssm list\r
 \r
 \r
+Exporting service configuration\r
+-------------------------------\r
+NSSM can dump commands which would recreate the configuration of a service.\r
+The output can be pasted into a batch script to back up the service or\r
+transfer to another computer.\r
+\r
+    nssm dump <servicename>\r
+\r
+Because the service configuration may contain characters which need to be\r
+quoted or escaped from the command prompt, NSSM tries hard to produce\r
+output which will work correctly when run as a script, by adding quotes\r
+and caret escapes as appropriate.\r
+\r
+\r
 Example usage\r
 -------------\r
 To install an Unreal Tournament server:\r
 Example usage\r
 -------------\r
 To install an Unreal Tournament server:\r
index b22cb92..9314753 100644 (file)
--- a/nssm.cpp
+++ b/nssm.cpp
@@ -265,7 +265,7 @@ int _tmain(int argc, TCHAR **argv) {
       create_messages();\r
       exit(pre_install_service(argc - 2, argv + 2));\r
     }\r
       create_messages();\r
       exit(pre_install_service(argc - 2, argv + 2));\r
     }\r
-    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"))) {\r
+    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")) || str_equiv(argv[1], _T("dump"))) {\r
       int ret = pre_edit_service(argc - 1, argv + 1);\r
       if (ret == 3 && ! is_admin && argc == 3) exit(elevate(argc, argv, NSSM_MESSAGE_NOT_ADMINISTRATOR_CANNOT_EDIT));\r
       /* There might be a password here. */\r
       int ret = pre_edit_service(argc - 1, argv + 1);\r
       if (ret == 3 && ! is_admin && argc == 3) exit(elevate(argc, argv, NSSM_MESSAGE_NOT_ADMINISTRATOR_CANNOT_EDIT));\r
       /* There might be a password here. */\r
index a94e698..8561616 100644 (file)
@@ -883,7 +883,7 @@ int pre_edit_service(int argc, TCHAR **argv) {
   if (argc < 2) return usage(1);\r
 \r
   /* Are we editing on the command line? */\r
   if (argc < 2) return usage(1);\r
 \r
   /* Are we editing on the command line? */\r
-  enum { MODE_EDITING, MODE_GETTING, MODE_SETTING, MODE_RESETTING } mode = MODE_EDITING;\r
+  enum { MODE_EDITING, MODE_GETTING, MODE_SETTING, MODE_RESETTING, MODE_DUMPING } mode = MODE_EDITING;\r
   const TCHAR *verb = argv[0];\r
   const TCHAR *service_name = argv[1];\r
   bool getting = false;\r
   const TCHAR *verb = argv[0];\r
   const TCHAR *service_name = argv[1];\r
   bool getting = false;\r
@@ -906,6 +906,7 @@ int pre_edit_service(int argc, TCHAR **argv) {
     mandatory = 3;\r
     mode = MODE_RESETTING;\r
   }\r
     mandatory = 3;\r
     mode = MODE_RESETTING;\r
   }\r
+  else if (str_equiv(verb, _T("dump"))) mode = MODE_DUMPING;\r
   if (argc < mandatory) return usage(1);\r
 \r
   const TCHAR *parameter = 0;\r
   if (argc < mandatory) return usage(1);\r
 \r
   const TCHAR *parameter = 0;\r
@@ -980,7 +981,7 @@ int pre_edit_service(int argc, TCHAR **argv) {
 \r
   service->type = qsc->dwServiceType;\r
   if (! (service->type & SERVICE_WIN32_OWN_PROCESS)) {\r
 \r
   service->type = qsc->dwServiceType;\r
   if (! (service->type & SERVICE_WIN32_OWN_PROCESS)) {\r
-    if (mode != MODE_GETTING) {\r
+    if (mode != MODE_GETTING && mode != MODE_DUMPING) {\r
       HeapFree(GetProcessHeap(), 0, qsc);\r
       CloseServiceHandle(service->handle);\r
       CloseServiceHandle(services);\r
       HeapFree(GetProcessHeap(), 0, qsc);\r
       CloseServiceHandle(service->handle);\r
       CloseServiceHandle(services);\r
@@ -990,7 +991,7 @@ int pre_edit_service(int argc, TCHAR **argv) {
   }\r
 \r
   if (get_service_startup(service->name, service->handle, qsc, &service->startup)) {\r
   }\r
 \r
   if (get_service_startup(service->name, service->handle, qsc, &service->startup)) {\r
-    if (mode != MODE_GETTING) {\r
+    if (mode != MODE_GETTING && mode != MODE_DUMPING) {\r
       HeapFree(GetProcessHeap(), 0, qsc);\r
       CloseServiceHandle(service->handle);\r
       CloseServiceHandle(services);\r
       HeapFree(GetProcessHeap(), 0, qsc);\r
       CloseServiceHandle(service->handle);\r
       CloseServiceHandle(services);\r
@@ -999,7 +1000,7 @@ int pre_edit_service(int argc, TCHAR **argv) {
   }\r
 \r
   if (get_service_username(service->name, qsc, &service->username, &service->usernamelen)) {\r
   }\r
 \r
   if (get_service_username(service->name, qsc, &service->username, &service->usernamelen)) {\r
-    if (mode != MODE_GETTING) {\r
+    if (mode != MODE_GETTING && mode != MODE_DUMPING) {\r
       HeapFree(GetProcessHeap(), 0, qsc);\r
       CloseServiceHandle(service->handle);\r
       CloseServiceHandle(services);\r
       HeapFree(GetProcessHeap(), 0, qsc);\r
       CloseServiceHandle(service->handle);\r
       CloseServiceHandle(services);\r
@@ -1019,7 +1020,7 @@ int pre_edit_service(int argc, TCHAR **argv) {
 \r
   /* Get extended system details. */\r
   if (get_service_description(service->name, service->handle, _countof(service->description), service->description)) {\r
 \r
   /* Get extended system details. */\r
   if (get_service_description(service->name, service->handle, _countof(service->description), service->description)) {\r
-    if (mode != MODE_GETTING) {\r
+    if (mode != MODE_GETTING && mode != MODE_DUMPING) {\r
       CloseServiceHandle(service->handle);\r
       CloseServiceHandle(services);\r
       return 6;\r
       CloseServiceHandle(service->handle);\r
       CloseServiceHandle(services);\r
       return 6;\r
@@ -1027,7 +1028,7 @@ int pre_edit_service(int argc, TCHAR **argv) {
   }\r
 \r
   if (get_service_dependencies(service->name, service->handle, &service->dependencies, &service->dependencieslen)) {\r
   }\r
 \r
   if (get_service_dependencies(service->name, service->handle, &service->dependencies, &service->dependencieslen)) {\r
-    if (mode != MODE_GETTING) {\r
+    if (mode != MODE_GETTING && mode != MODE_DUMPING) {\r
       CloseServiceHandle(service->handle);\r
       CloseServiceHandle(services);\r
       return 7;\r
       CloseServiceHandle(service->handle);\r
       CloseServiceHandle(services);\r
       return 7;\r
@@ -1041,7 +1042,7 @@ int pre_edit_service(int argc, TCHAR **argv) {
 \r
   if (! service->exe[0]) {\r
     service->native = true;\r
 \r
   if (! service->exe[0]) {\r
     service->native = true;\r
-    if (mode != MODE_GETTING) print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE, service->name, NSSM, service->image);\r
+    if (mode != MODE_GETTING && mode != MODE_DUMPING) print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE, service->name, NSSM, service->image);\r
   }\r
 \r
   /* Editing with the GUI. */\r
   }\r
 \r
   /* Editing with the GUI. */\r
@@ -1050,6 +1051,31 @@ int pre_edit_service(int argc, TCHAR **argv) {
     return 0;\r
   }\r
 \r
     return 0;\r
   }\r
 \r
+  HKEY key;\r
+  value_t value;\r
+  int ret;\r
+\r
+  if (mode == MODE_DUMPING) {\r
+    if (service->native) key = 0;\r
+    else {\r
+      key = open_registry(service->name, KEY_READ);\r
+      if (! key) return 4;\r
+    }\r
+\r
+    ret = 0;\r
+    for (i = 0; settings[i].name; i++) {\r
+      setting = &settings[i];\r
+      if (! setting->native && service->native) continue;\r
+      if (dump_setting(service->name, key, service->handle, setting)) ret++;\r
+    }\r
+\r
+    if (! service->native) RegCloseKey(key);\r
+    CloseServiceHandle(service->handle);\r
+\r
+    if (ret) return 1;\r
+    return 0;\r
+  }\r
+\r
   /* Trying to manage App* parameters for a non-NSSM service. */\r
   if (! setting->native && service->native) {\r
     CloseServiceHandle(service->handle);\r
   /* Trying to manage App* parameters for a non-NSSM service. */\r
   if (! setting->native && service->native) {\r
     CloseServiceHandle(service->handle);\r
@@ -1057,10 +1083,6 @@ int pre_edit_service(int argc, TCHAR **argv) {
     return 1;\r
   }\r
 \r
     return 1;\r
   }\r
 \r
-  HKEY key;\r
-  value_t value;\r
-  int ret;\r
-\r
   if (mode == MODE_GETTING) {\r
     if (! service->native) {\r
       key = open_registry(service->name, KEY_READ);\r
   if (mode == MODE_GETTING) {\r
     if (! service->native) {\r
       key = open_registry(service->name, KEY_READ);\r
@@ -1083,7 +1105,7 @@ int pre_edit_service(int argc, TCHAR **argv) {
         break;\r
 \r
       case REG_DWORD:\r
         break;\r
 \r
       case REG_DWORD:\r
-        _tprintf(_T("%u\n"), value.numeric);\r
+        _tprintf(_T("%lu\n"), value.numeric);\r
         break;\r
     }\r
 \r
         break;\r
     }\r
 \r
index c825fbc..f22a04d 100644 (file)
@@ -17,6 +17,14 @@ static inline int is_default(const TCHAR *value) {
   return (str_equiv(value, NSSM_DEFAULT_STRING) || str_equiv(value, _T("*")) || ! value[0]);\r
 }\r
 \r
   return (str_equiv(value, NSSM_DEFAULT_STRING) || str_equiv(value, _T("*")) || ! value[0]);\r
 }\r
 \r
+/* What type of parameter is it parameter? */\r
+static inline bool is_string_type(const unsigned long type) {\r
+  return (type == REG_MULTI_SZ || type == REG_EXPAND_SZ || type == REG_SZ);\r
+}\r
+static inline bool is_numeric_type(const unsigned long type) {\r
+  return (type == REG_DWORD);\r
+}\r
+\r
 static int value_from_string(const TCHAR *name, value_t *value, const TCHAR *string) {\r
   size_t len = _tcslen(string);\r
   if (! len++) {\r
 static int value_from_string(const TCHAR *name, value_t *value, const TCHAR *string) {\r
   size_t len = _tcslen(string);\r
   if (! len++) {\r
@@ -110,6 +118,42 @@ static int setting_get_string(const TCHAR *service_name, void *param, const TCHA
   return value_from_string(name, value, buffer);\r
 }\r
 \r
   return value_from_string(name, value, buffer);\r
 }\r
 \r
+static int setting_not_dumpable(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
+  return 0;\r
+}\r
+\r
+static int setting_dump_string(const TCHAR *service_name, void *param, const TCHAR *name, const value_t *value, const TCHAR *additional) {\r
+  TCHAR quoted_service_name[SERVICE_NAME_LENGTH * 2];\r
+  TCHAR quoted_value[VALUE_LENGTH * 2];\r
+  TCHAR quoted_additional[VALUE_LENGTH * 2];\r
+  TCHAR quoted_nssm[EXE_LENGTH * 2];\r
+\r
+  if (quote(service_name, quoted_service_name, _countof(quoted_service_name))) return 1;\r
+\r
+  if (additional) {\r
+    if (_tcslen(additional)) {\r
+      if (quote(additional, quoted_additional, _countof(quoted_additional))) return 3;\r
+    }\r
+  else _sntprintf_s(quoted_additional, _countof(quoted_additional), _TRUNCATE, _T("\"\""));\r
+  }\r
+  else quoted_additional[0] = _T('\0');\r
+\r
+  unsigned long type = (unsigned long) param;\r
+  if (is_string_type(type)) {\r
+    if (_tcslen(value->string)) {\r
+      if (quote(value->string, quoted_value, _countof(quoted_value))) return 2;\r
+    }\r
+    else _sntprintf_s(quoted_value, _countof(quoted_value), _TRUNCATE, _T("\"\""));\r
+  }\r
+  else if (is_numeric_type(type)) _sntprintf_s(quoted_value, _countof(quoted_value), _TRUNCATE, _T("%lu"), value->numeric);\r
+  else return 2;\r
+\r
+  if (quote(nssm_exe(), quoted_nssm, _countof(quoted_nssm))) return 3;\r
+  if (_tcslen(quoted_additional)) _tprintf(_T("%s set %s %s %s %s\n"), quoted_nssm, quoted_service_name, name, quoted_additional, quoted_value);\r
+  else _tprintf(_T("%s set %s %s %s\n"), quoted_nssm, quoted_service_name, name, quoted_value);\r
+  return 0;\r
+}\r
+\r
 static int setting_set_exit_action(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
   unsigned long exitcode;\r
   TCHAR *code;\r
 static int setting_set_exit_action(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
   unsigned long exitcode;\r
   TCHAR *code;\r
@@ -190,6 +234,40 @@ static int setting_get_exit_action(const TCHAR *service_name, void *param, const
   return 1;\r
 }\r
 \r
   return 1;\r
 }\r
 \r
+static int setting_dump_exit_action(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
+  int errors = 0;\r
+  HKEY key = open_registry(service_name, NSSM_REG_EXIT, KEY_READ);\r
+  if (! key) return -1;\r
+\r
+  TCHAR code[16];\r
+  unsigned long index = 0;\r
+  while (true) {\r
+    int ret = enumerate_registry_values(key, &index, code, _countof(code));\r
+    if (ret == ERROR_NO_MORE_ITEMS) break;\r
+    if (ret != ERROR_SUCCESS) continue;\r
+    bool valid = true;\r
+    int i;\r
+    for (i = 0; i < _countof(code); i++) {\r
+      if (! code[i]) break;\r
+      if (code[i] >= _T('0') || code[i] <= _T('9')) continue;\r
+      valid = false;\r
+      break;\r
+    }\r
+    if (! valid) continue;\r
+\r
+    TCHAR *additional = (code[i]) ? code : NSSM_DEFAULT_STRING;\r
+\r
+    ret = setting_get_exit_action(service_name, 0, name, default_value, value, additional);\r
+    if (ret == 1) {\r
+      if (setting_dump_string(service_name, (void *) REG_SZ, name, value, additional)) errors++;\r
+    }\r
+    else if (ret < 0) errors++;\r
+  }\r
+\r
+  if (errors) return -1;\r
+  return 0;\r
+}\r
+\r
 static inline bool split_hook_name(const TCHAR *hook_name, TCHAR *hook_event, TCHAR *hook_action) {\r
   TCHAR *s;\r
 \r
 static inline bool split_hook_name(const TCHAR *hook_name, TCHAR *hook_event, TCHAR *hook_action) {\r
   TCHAR *s;\r
 \r
@@ -235,6 +313,33 @@ static int setting_get_hook(const TCHAR *service_name, void *param, const TCHAR
   return 1;\r
 }\r
 \r
   return 1;\r
 }\r
 \r
+static int setting_dump_hooks(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
+  int i, j;\r
+\r
+  int errors = 0;\r
+  for (i = 0; hook_event_strings[i]; i++) {\r
+    const TCHAR *hook_event = hook_event_strings[i];\r
+    for (j = 0; hook_action_strings[j]; j++) {\r
+      const TCHAR *hook_action = hook_action_strings[j];\r
+      if (! valid_hook_name(hook_event, hook_action, true)) continue;\r
+\r
+      TCHAR hook_name[HOOK_NAME_LENGTH];\r
+      _sntprintf_s(hook_name, _countof(hook_name), _TRUNCATE, _T("%s/%s"), hook_event, hook_action);\r
+\r
+      int ret = setting_get_hook(service_name, param, name, default_value, value, hook_name);\r
+      if (ret != 1) {\r
+        if (ret < 0) errors++;\r
+        continue;\r
+      }\r
+\r
+      if (setting_dump_string(service_name, (void *) REG_SZ, name, value, hook_name)) errors++;\r
+    }\r
+  }\r
+\r
+  if (errors) return -1;\r
+  return 0;\r
+}\r
+\r
 static int setting_set_affinity(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
   HKEY key = (HKEY) param;\r
   if (! key) return -1;\r
 static int setting_set_affinity(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
   HKEY key = (HKEY) param;\r
   if (! key) return -1;\r
@@ -451,6 +556,39 @@ static int setting_get_environment(const TCHAR *service_name, void *param, const
   return ret;\r
 }\r
 \r
   return ret;\r
 }\r
 \r
+static int setting_dump_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
+  int errors = 0;\r
+  HKEY key = (HKEY) param;\r
+  if (! param) return -1;\r
+\r
+  TCHAR *env = 0;\r
+  unsigned long envlen;\r
+  if (get_environment((TCHAR *) service_name, key, (TCHAR *) name, &env, &envlen)) return -1;\r
+  if (! envlen) return 0;\r
+\r
+  TCHAR *s;\r
+  for (s = env; *s; s++) {\r
+    size_t len = _tcslen(s) + 2;\r
+    value->string = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR));\r
+    if (! value->string) {\r
+      print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("dump"), _T("setting_dump_environment"));\r
+      break;\r
+    }\r
+\r
+    _sntprintf_s(value->string, len, _TRUNCATE, _T("%c%s"), (s > env) ? _T('+') : _T(':'), s);\r
+    if (setting_dump_string(service_name, (void *) REG_SZ, name, value, 0)) errors++;\r
+    HeapFree(GetProcessHeap(), 0, value->string);\r
+    value->string = 0;\r
+\r
+    for ( ; *s; s++);\r
+  }\r
+\r
+  HeapFree(GetProcessHeap(), 0, env);\r
+\r
+  if (errors) return 1;\r
+  return 0;\r
+}\r
+\r
 static int setting_set_priority(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
   HKEY key = (HKEY) param;\r
   if (! param) return -1;\r
 static int setting_set_priority(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
   HKEY key = (HKEY) param;\r
   if (! param) return -1;\r
@@ -503,6 +641,13 @@ static int setting_get_priority(const TCHAR *service_name, void *param, const TC
   return value_from_string(name, value, priority_strings[priority_constant_to_index(constant)]);\r
 }\r
 \r
   return value_from_string(name, value, priority_strings[priority_constant_to_index(constant)]);\r
 }\r
 \r
+static int setting_dump_priority(const TCHAR *service_name, void *key_ptr, const TCHAR *name, void *setting_ptr, value_t *value, const TCHAR *additional) {\r
+  settings_t *setting = (settings_t *) setting_ptr;\r
+  int ret = setting_get_priority(service_name, key_ptr, name, (void *) setting->default_value, value, 0);\r
+  if (ret != 1) return ret;\r
+  return setting_dump_string(service_name, (void *) REG_SZ, name, value, 0);\r
+}\r
+\r
 /* Functions to manage native service settings. */\r
 static int native_set_dependon(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR **dependencies, unsigned long *dependencieslen, value_t *value, int type) {\r
   *dependencieslen = 0;\r
 /* Functions to manage native service settings. */\r
 static int native_set_dependon(const TCHAR *service_name, SC_HANDLE service_handle, TCHAR **dependencies, unsigned long *dependencieslen, value_t *value, int type) {\r
   *dependencieslen = 0;\r
@@ -670,6 +815,41 @@ static int native_get_dependongroup(const TCHAR *service_name, void *param, cons
   return ret;\r
 }\r
 \r
   return ret;\r
 }\r
 \r
+static int setting_dump_dependon(const TCHAR *service_name, SC_HANDLE service_handle, const TCHAR *name, int type, value_t *value) {\r
+  int errors = 0;\r
+\r
+  TCHAR *dependencies = 0;\r
+  unsigned long dependencieslen;\r
+  if (get_service_dependencies(service_name, service_handle, &dependencies, &dependencieslen, type)) return -1;\r
+  if (! dependencieslen) return 0;\r
+\r
+  TCHAR *s;\r
+  for (s = dependencies; *s; s++) {\r
+    size_t len = _tcslen(s) + 2;\r
+    value->string = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR));\r
+    if (! value->string) {\r
+      print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("dump"), _T("setting_dump_dependon"));\r
+      break;\r
+    }\r
+\r
+    _sntprintf_s(value->string, len, _TRUNCATE, _T("%c%s"), (s > dependencies) ? _T('+') : _T(':'), s);\r
+    if (setting_dump_string(service_name, (void *) REG_SZ, name, value, 0)) errors++;\r
+    HeapFree(GetProcessHeap(), 0, value->string);\r
+    value->string = 0;\r
+\r
+    for ( ; *s; s++);\r
+  }\r
+\r
+  HeapFree(GetProcessHeap(), 0, dependencies);\r
+\r
+  if (errors) return 1;\r
+  return 0;\r
+}\r
+\r
+static int native_dump_dependongroup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
+  return setting_dump_dependon(service_name, (SC_HANDLE) param, name, DEPENDENCY_GROUPS, value);\r
+}\r
+\r
 static int native_set_dependonservice(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
   SC_HANDLE service_handle = (SC_HANDLE) param;\r
   if (! service_handle) return -1;\r
 static int native_set_dependonservice(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
   SC_HANDLE service_handle = (SC_HANDLE) param;\r
   if (! service_handle) return -1;\r
@@ -750,6 +930,10 @@ static int native_get_dependonservice(const TCHAR *service_name, void *param, co
   return ret;\r
 }\r
 \r
   return ret;\r
 }\r
 \r
+static int native_dump_dependonservice(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
+  return setting_dump_dependon(service_name, (SC_HANDLE) param, name, DEPENDENCY_SERVICES, value);\r
+}\r
+\r
 int native_set_description(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
   SC_HANDLE service_handle = (SC_HANDLE) param;\r
   if (! service_handle) return -1;\r
 int native_set_description(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
   SC_HANDLE service_handle = (SC_HANDLE) param;\r
   if (! service_handle) return -1;\r
@@ -832,6 +1016,15 @@ int native_get_environment(const TCHAR *service_name, void *param, const TCHAR *
   return ret;\r
 }\r
 \r
   return ret;\r
 }\r
 \r
+static int native_dump_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
+  HKEY key = open_service_registry(service_name, KEY_READ, true);\r
+  if (! key) return -1;\r
+\r
+  int ret = setting_dump_environment(service_name, (void *) key, name, default_value, value, additional);\r
+  RegCloseKey(key);\r
+  return ret;\r
+}\r
+\r
 int native_set_imagepath(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
   SC_HANDLE service_handle = (SC_HANDLE) param;\r
   if (! service_handle) return -1;\r
 int native_set_imagepath(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
   SC_HANDLE service_handle = (SC_HANDLE) param;\r
   if (! service_handle) return -1;\r
@@ -953,6 +1146,20 @@ int native_get_objectname(const TCHAR *service_name, void *param, const TCHAR *n
   return ret;\r
 }\r
 \r
   return ret;\r
 }\r
 \r
+int native_dump_objectname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
+  int ret = native_get_objectname(service_name, param, name, default_value, value, additional);\r
+  if (ret != 1) return ret;\r
+\r
+  /* Do we need to dump a dummy password? */\r
+  if (! well_known_username(value->string)) {\r
+    /* Parameters are the other way round. */\r
+    value_t inverted;\r
+    inverted.string = _T("****");\r
+    return setting_dump_string(service_name, (void *) REG_SZ, name, &inverted, value->string);\r
+  }\r
+  return setting_dump_string(service_name, (void *) REG_SZ, name, value, 0);\r
+}\r
+\r
 int native_set_startup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
   SC_HANDLE service_handle = (SC_HANDLE) param;\r
   if (! service_handle) return -1;\r
 int native_set_startup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
   SC_HANDLE service_handle = (SC_HANDLE) param;\r
   if (! service_handle) return -1;\r
@@ -1135,25 +1342,17 @@ int get_setting(const TCHAR *service_name, HKEY key, settings_t *setting, value_
   if (! key) return -1;\r
   int ret;\r
 \r
   if (! key) return -1;\r
   int ret;\r
 \r
-  switch (setting->type) {\r
-    case REG_EXPAND_SZ:\r
-    case REG_MULTI_SZ:\r
-    case REG_SZ:\r
-      value->string = (TCHAR *) setting->default_value;\r
-      if (setting->get) ret = setting->get(service_name, (void *) key, setting->name, setting->default_value, value, additional);\r
-      else ret = -1;\r
-      break;\r
-\r
-    case REG_DWORD:\r
-      value->numeric = PtrToUlong(setting->default_value);\r
-      if (setting->get) ret = setting->get(service_name, (void *) key, setting->name, setting->default_value, value, additional);\r
-      else ret = -1;\r
-      break;\r
-\r
-    default:\r
-      ret = -1;\r
-      break;\r
+  if (is_string_type(setting->type)) {\r
+    value->string = (TCHAR *) setting->default_value;\r
+    if (setting->get) ret = setting->get(service_name, (void *) key, setting->name, setting->default_value, value, additional);\r
+    else ret = -1;\r
+  }\r
+  else if (is_numeric_type(setting->type)) {\r
+    value->numeric = PtrToUlong(setting->default_value);\r
+    if (setting->get) ret = setting->get(service_name, (void *) key, setting->name, setting->default_value, value, additional);\r
+    else ret = -1;\r
   }\r
   }\r
+  else ret = -1;\r
 \r
   if (ret < 0) print_message(stderr, NSSM_MESSAGE_GET_SETTING_FAILED, setting->name, service_name);\r
 \r
 \r
   if (ret < 0) print_message(stderr, NSSM_MESSAGE_GET_SETTING_FAILED, setting->name, service_name);\r
 \r
@@ -1165,54 +1364,75 @@ int get_setting(const TCHAR *service_name, SC_HANDLE service_handle, settings_t
   return setting->get(service_name, service_handle, setting->name, 0, value, additional);\r
 }\r
 \r
   return setting->get(service_name, service_handle, setting->name, 0, value, additional);\r
 }\r
 \r
+int dump_setting(const TCHAR *service_name, HKEY key, SC_HANDLE service_handle, settings_t *setting) {\r
+  void *param;\r
+  if (setting->native) {\r
+    if (! service_handle) return -1;\r
+    param = (void *) service_handle;\r
+  }\r
+  else {\r
+    /* Will be null for native services. */\r
+    param = (void *) key;\r
+  }\r
+\r
+  value_t value = { 0 };\r
+  int ret;\r
+\r
+  if (setting->dump) return setting->dump(service_name, param, setting->name, (void *) setting, &value, 0);\r
+  if (setting->native) ret = get_setting(service_name, service_handle, setting, &value, 0);\r
+  else ret = get_setting(service_name, key, setting, &value, 0);\r
+  if (ret != 1) return ret;\r
+  return setting_dump_string(service_name, (void *) setting->type, setting->name, &value, 0);\r
+}\r
+\r
 settings_t settings[] = {\r
 settings_t settings[] = {\r
-  { NSSM_REG_EXE, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string },\r
-  { NSSM_REG_FLAGS, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string },\r
-  { NSSM_REG_DIR, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string },\r
-  { NSSM_REG_EXIT, REG_SZ, (void *) exit_action_strings[NSSM_EXIT_RESTART], false, ADDITIONAL_MANDATORY, setting_set_exit_action, setting_get_exit_action },\r
-  { NSSM_REG_HOOK, REG_SZ, (void *) _T(""), false, ADDITIONAL_MANDATORY, setting_set_hook, setting_get_hook },\r
-  { NSSM_REG_AFFINITY, REG_SZ, 0, false, 0, setting_set_affinity, setting_get_affinity },\r
-  { NSSM_REG_ENV, REG_MULTI_SZ, NULL, false, ADDITIONAL_CRLF, setting_set_environment, setting_get_environment },\r
-  { NSSM_REG_ENV_EXTRA, REG_MULTI_SZ, NULL, false, ADDITIONAL_CRLF, setting_set_environment, setting_get_environment },\r
-  { NSSM_REG_NO_CONSOLE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },\r
-  { NSSM_REG_PRIORITY, REG_SZ, (void *) priority_strings[NSSM_NORMAL_PRIORITY], false, 0, setting_set_priority, setting_get_priority },\r
-  { NSSM_REG_RESTART_DELAY, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },\r
-  { NSSM_REG_STDIN, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },\r
-  { NSSM_REG_STDIN NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDIN_SHARING, false, 0, setting_set_number, setting_get_number },\r
-  { NSSM_REG_STDIN NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDIN_DISPOSITION, false, 0, setting_set_number, setting_get_number },\r
-  { NSSM_REG_STDIN NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDIN_FLAGS, false, 0, setting_set_number, setting_get_number },\r
-  { NSSM_REG_STDOUT, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },\r
-  { NSSM_REG_STDOUT NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDOUT_SHARING, false, 0, setting_set_number, setting_get_number },\r
-  { NSSM_REG_STDOUT NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDOUT_DISPOSITION, false, 0, setting_set_number, setting_get_number },\r
-  { NSSM_REG_STDOUT NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDOUT_FLAGS, false, 0, setting_set_number, setting_get_number },\r
-  { NSSM_REG_STDOUT NSSM_REG_STDIO_COPY_AND_TRUNCATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },\r
-  { NSSM_REG_STDERR, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },\r
-  { NSSM_REG_STDERR NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDERR_SHARING, false, 0, setting_set_number, setting_get_number },\r
-  { NSSM_REG_STDERR NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDERR_DISPOSITION, false, 0, setting_set_number, setting_get_number },\r
-  { NSSM_REG_STDERR NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDERR_FLAGS, false, 0, setting_set_number, setting_get_number },\r
-  { NSSM_REG_STDERR NSSM_REG_STDIO_COPY_AND_TRUNCATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },\r
-  { NSSM_REG_STOP_METHOD_SKIP, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },\r
-  { NSSM_REG_KILL_CONSOLE_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_CONSOLE_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number },\r
-  { NSSM_REG_KILL_WINDOW_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_WINDOW_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number },\r
-  { NSSM_REG_KILL_THREADS_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_THREADS_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number },\r
-  { NSSM_REG_KILL_PROCESS_TREE, REG_DWORD, (void *) 1, false, 0, setting_set_number, setting_get_number },\r
-  { NSSM_REG_THROTTLE, REG_DWORD, (void *) NSSM_RESET_THROTTLE_RESTART, false, 0, setting_set_number, setting_get_number },\r
-  { NSSM_REG_HOOK_SHARE_OUTPUT_HANDLES, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },\r
-  { NSSM_REG_ROTATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },\r
-  { NSSM_REG_ROTATE_ONLINE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },\r
-  { NSSM_REG_ROTATE_SECONDS, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },\r
-  { NSSM_REG_ROTATE_BYTES_LOW, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },\r
-  { NSSM_REG_ROTATE_BYTES_HIGH, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },\r
-  { NSSM_REG_ROTATE_DELAY, REG_DWORD, (void *) NSSM_ROTATE_DELAY, false, 0, setting_set_number, setting_get_number },\r
-  { NSSM_NATIVE_DEPENDONGROUP, REG_MULTI_SZ, NULL, true, ADDITIONAL_CRLF, native_set_dependongroup, native_get_dependongroup },\r
-  { NSSM_NATIVE_DEPENDONSERVICE, REG_MULTI_SZ, NULL, true, ADDITIONAL_CRLF, native_set_dependonservice, native_get_dependonservice },\r
-  { NSSM_NATIVE_DESCRIPTION, REG_SZ, _T(""), true, 0, native_set_description, native_get_description },\r
-  { NSSM_NATIVE_DISPLAYNAME, REG_SZ, NULL, true, 0, native_set_displayname, native_get_displayname },\r
-  { NSSM_NATIVE_ENVIRONMENT, REG_MULTI_SZ, NULL, true, ADDITIONAL_CRLF, native_set_environment, native_get_environment },\r
-  { NSSM_NATIVE_IMAGEPATH, REG_EXPAND_SZ, NULL, true, 0, native_set_imagepath, native_get_imagepath },\r
-  { NSSM_NATIVE_OBJECTNAME, REG_SZ, NSSM_LOCALSYSTEM_ACCOUNT, true, 0, native_set_objectname, native_get_objectname },\r
-  { NSSM_NATIVE_NAME, REG_SZ, NULL, true, 0, native_set_name, native_get_name },\r
-  { NSSM_NATIVE_STARTUP, REG_SZ, NULL, true, 0, native_set_startup, native_get_startup },\r
-  { NSSM_NATIVE_TYPE, REG_SZ, NULL, true, 0, native_set_type, native_get_type },\r
+  { NSSM_REG_EXE, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string, 0 },\r
+  { NSSM_REG_FLAGS, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string, 0 },\r
+  { NSSM_REG_DIR, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string, 0 },\r
+  { NSSM_REG_EXIT, REG_SZ, (void *) exit_action_strings[NSSM_EXIT_RESTART], false, ADDITIONAL_MANDATORY, setting_set_exit_action, setting_get_exit_action, setting_dump_exit_action },\r
+  { NSSM_REG_HOOK, REG_SZ, (void *) _T(""), false, ADDITIONAL_MANDATORY, setting_set_hook, setting_get_hook, setting_dump_hooks },\r
+  { NSSM_REG_AFFINITY, REG_SZ, 0, false, 0, setting_set_affinity, setting_get_affinity, 0 },\r
+  { NSSM_REG_ENV, REG_MULTI_SZ, NULL, false, ADDITIONAL_CRLF, setting_set_environment, setting_get_environment, setting_dump_environment },\r
+  { NSSM_REG_ENV_EXTRA, REG_MULTI_SZ, NULL, false, ADDITIONAL_CRLF, setting_set_environment, setting_get_environment, setting_dump_environment },\r
+  { NSSM_REG_NO_CONSOLE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },\r
+  { NSSM_REG_PRIORITY, REG_SZ, (void *) priority_strings[NSSM_NORMAL_PRIORITY], false, 0, setting_set_priority, setting_get_priority, setting_dump_priority },\r
+  { NSSM_REG_RESTART_DELAY, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },\r
+  { NSSM_REG_STDIN, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string, 0 },\r
+  { NSSM_REG_STDIN NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDIN_SHARING, false, 0, setting_set_number, setting_get_number, 0 },\r
+  { NSSM_REG_STDIN NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDIN_DISPOSITION, false, 0, setting_set_number, setting_get_number, 0 },\r
+  { NSSM_REG_STDIN NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDIN_FLAGS, false, 0, setting_set_number, setting_get_number, 0 },\r
+  { NSSM_REG_STDOUT, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string, 0 },\r
+  { NSSM_REG_STDOUT NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDOUT_SHARING, false, 0, setting_set_number, setting_get_number, 0 },\r
+  { NSSM_REG_STDOUT NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDOUT_DISPOSITION, false, 0, setting_set_number, setting_get_number, 0 },\r
+  { NSSM_REG_STDOUT NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDOUT_FLAGS, false, 0, setting_set_number, setting_get_number, 0 },\r
+  { NSSM_REG_STDOUT NSSM_REG_STDIO_COPY_AND_TRUNCATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },\r
+  { NSSM_REG_STDERR, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string, 0 },\r
+  { NSSM_REG_STDERR NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDERR_SHARING, false, 0, setting_set_number, setting_get_number, 0 },\r
+  { NSSM_REG_STDERR NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDERR_DISPOSITION, false, 0, setting_set_number, setting_get_number, 0 },\r
+  { NSSM_REG_STDERR NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDERR_FLAGS, false, 0, setting_set_number, setting_get_number, 0 },\r
+  { NSSM_REG_STDERR NSSM_REG_STDIO_COPY_AND_TRUNCATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },\r
+  { NSSM_REG_STOP_METHOD_SKIP, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },\r
+  { NSSM_REG_KILL_CONSOLE_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_CONSOLE_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number, 0 },\r
+  { NSSM_REG_KILL_WINDOW_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_WINDOW_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number, 0 },\r
+  { NSSM_REG_KILL_THREADS_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_THREADS_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number, 0 },\r
+  { NSSM_REG_KILL_PROCESS_TREE, REG_DWORD, (void *) 1, false, 0, setting_set_number, setting_get_number, 0 },\r
+  { NSSM_REG_THROTTLE, REG_DWORD, (void *) NSSM_RESET_THROTTLE_RESTART, false, 0, setting_set_number, setting_get_number, 0 },\r
+  { NSSM_REG_HOOK_SHARE_OUTPUT_HANDLES, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },\r
+  { NSSM_REG_ROTATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },\r
+  { NSSM_REG_ROTATE_ONLINE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },\r
+  { NSSM_REG_ROTATE_SECONDS, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },\r
+  { NSSM_REG_ROTATE_BYTES_LOW, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },\r
+  { NSSM_REG_ROTATE_BYTES_HIGH, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number, 0 },\r
+  { NSSM_REG_ROTATE_DELAY, REG_DWORD, (void *) NSSM_ROTATE_DELAY, false, 0, setting_set_number, setting_get_number, 0 },\r
+  { NSSM_NATIVE_DEPENDONGROUP, REG_MULTI_SZ, NULL, true, ADDITIONAL_CRLF, native_set_dependongroup, native_get_dependongroup, native_dump_dependongroup },\r
+  { NSSM_NATIVE_DEPENDONSERVICE, REG_MULTI_SZ, NULL, true, ADDITIONAL_CRLF, native_set_dependonservice, native_get_dependonservice, native_dump_dependonservice },\r
+  { NSSM_NATIVE_DESCRIPTION, REG_SZ, _T(""), true, 0, native_set_description, native_get_description, 0 },\r
+  { NSSM_NATIVE_DISPLAYNAME, REG_SZ, NULL, true, 0, native_set_displayname, native_get_displayname, 0 },\r
+  { NSSM_NATIVE_ENVIRONMENT, REG_MULTI_SZ, NULL, true, ADDITIONAL_CRLF, native_set_environment, native_get_environment, native_dump_environment },\r
+  { NSSM_NATIVE_IMAGEPATH, REG_EXPAND_SZ, NULL, true, 0, native_set_imagepath, native_get_imagepath, setting_not_dumpable },\r
+  { NSSM_NATIVE_OBJECTNAME, REG_SZ, NSSM_LOCALSYSTEM_ACCOUNT, true, 0, native_set_objectname, native_get_objectname, native_dump_objectname },\r
+  { NSSM_NATIVE_NAME, REG_SZ, NULL, true, 0, native_set_name, native_get_name, setting_not_dumpable },\r
+  { NSSM_NATIVE_STARTUP, REG_SZ, NULL, true, 0, native_set_startup, native_get_startup, 0 },\r
+  { NSSM_NATIVE_TYPE, REG_SZ, NULL, true, 0, native_set_type, native_get_type, 0 },\r
   { NULL, NULL, NULL, NULL, NULL }\r
 };\r
   { NULL, NULL, NULL, NULL, NULL }\r
 };\r
index c28df38..80b5a35 100644 (file)
@@ -38,11 +38,13 @@ typedef struct {
   int additional;\r
   setting_function_t set;\r
   setting_function_t get;\r
   int additional;\r
   setting_function_t set;\r
   setting_function_t get;\r
+  setting_function_t dump;\r
 } settings_t;\r
 \r
 int set_setting(const TCHAR *, HKEY, settings_t *, value_t *, const TCHAR *);\r
 int set_setting(const TCHAR *, SC_HANDLE, settings_t *, value_t *, const TCHAR *);\r
 int get_setting(const TCHAR *, HKEY, settings_t *, value_t *, const TCHAR *);\r
 int get_setting(const TCHAR *, SC_HANDLE, settings_t *, value_t *, const TCHAR *);\r
 } settings_t;\r
 \r
 int set_setting(const TCHAR *, HKEY, settings_t *, value_t *, const TCHAR *);\r
 int set_setting(const TCHAR *, SC_HANDLE, settings_t *, value_t *, const TCHAR *);\r
 int get_setting(const TCHAR *, HKEY, settings_t *, value_t *, const TCHAR *);\r
 int get_setting(const TCHAR *, SC_HANDLE, settings_t *, value_t *, const TCHAR *);\r
+int dump_setting(const TCHAR *, HKEY, SC_HANDLE, settings_t *);\r
 \r
 #endif\r
 \r
 #endif\r