Use CRLF consistently.
authorIain Patterson <me@iain.cx>
Sun, 28 Feb 2016 08:59:11 +0000 (08:59 +0000)
committerIain Patterson <me@iain.cx>
Sun, 28 Feb 2016 08:59:11 +0000 (08:59 +0000)
The mismatch of line endings has long been an annoyance.

Thanks Mathias Breiner for advocating finally doing something about it.

15 files changed:
account.cpp
account.h
console.cpp
console.h
env.cpp
env.h
hook.cpp
hook.h
imports.cpp
imports.h
messages.mc
process.cpp
process.h
settings.cpp
settings.h

index 70bfa3e..60d1fc6 100644 (file)
-#include "nssm.h"
-
-#include <sddl.h>
-
-extern imports_t imports;
-
-/* Open Policy object. */
-int open_lsa_policy(LSA_HANDLE *policy) {
-  LSA_OBJECT_ATTRIBUTES attributes;
-  ZeroMemory(&attributes, sizeof(attributes));
-
-  NTSTATUS status = LsaOpenPolicy(0, &attributes, POLICY_ALL_ACCESS, policy);
-  if (status) {
-    print_message(stderr, NSSM_MESSAGE_LSAOPENPOLICY_FAILED, error_string(LsaNtStatusToWinError(status)));
-    return 1;
-  }
-
-  return 0;
-}
-
-/* Look up SID for an account. */
-int username_sid(const TCHAR *username, SID **sid, LSA_HANDLE *policy) {
-  LSA_HANDLE handle;
-  if (! policy) {
-    policy = &handle;
-    if (open_lsa_policy(policy)) return 1;
-  }
-
-  /*
-    LsaLookupNames() can't look up .\username but can look up
-    %COMPUTERNAME%\username.  ChangeServiceConfig() writes .\username to the
-    registry when %COMPUTERNAME%\username is a passed as a parameter.  We
-    need to preserve .\username when calling ChangeServiceConfig() without
-    changing the username, but expand to %COMPUTERNAME%\username when calling
-    LsaLookupNames().
-  */
-  TCHAR *expanded;
-  unsigned long expandedlen;
-  if (_tcsnicmp(_T(".\\"), username, 2)) {
-    expandedlen = (unsigned long) (_tcslen(username) + 1) * sizeof(TCHAR);
-    expanded = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, expandedlen);
-    if (! expanded) {
-      print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("expanded"), _T("username_sid"));
-      if (policy == &handle) LsaClose(handle);
-      return 2;
-    }
-    memmove(expanded, username, expandedlen);
-  }
-  else {
-    TCHAR computername[MAX_COMPUTERNAME_LENGTH + 1];
-    expandedlen = _countof(computername);
-    GetComputerName(computername, &expandedlen);
-    expandedlen += (unsigned long) _tcslen(username);
-
-    expanded = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, expandedlen * sizeof(TCHAR));
-    if (! expanded) {
-      print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("expanded"), _T("username_sid"));
-      if (policy == &handle) LsaClose(handle);
-      return 2;
-    }
-    _sntprintf_s(expanded, expandedlen, _TRUNCATE, _T("%s\\%s"), computername, username + 2);
-  }
-
-  LSA_UNICODE_STRING lsa_username;
-#ifdef UNICODE
-  lsa_username.Buffer = (wchar_t *) expanded;
-  lsa_username.Length = (unsigned short) _tcslen(expanded) * sizeof(TCHAR);
-  lsa_username.MaximumLength = lsa_username.Length + sizeof(TCHAR);
-#else
-  size_t buflen;
-  mbstowcs_s(&buflen, NULL, 0, expanded, _TRUNCATE);
-  lsa_username.MaximumLength = (unsigned short) buflen * sizeof(wchar_t);
-  lsa_username.Length = lsa_username.MaximumLength - sizeof(wchar_t);
-  lsa_username.Buffer = (wchar_t *) HeapAlloc(GetProcessHeap(), 0, lsa_username.MaximumLength);
-  if (lsa_username.Buffer) mbstowcs_s(&buflen, lsa_username.Buffer, lsa_username.MaximumLength, expanded, _TRUNCATE);
-  else {
-    if (policy == &handle) LsaClose(handle);
-    HeapFree(GetProcessHeap(), 0, expanded);
-    print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("LSA_UNICODE_STRING"), _T("username_sid()"));
-    return 4;
-  }
-#endif
-
-  LSA_REFERENCED_DOMAIN_LIST *translated_domains;
-  LSA_TRANSLATED_SID *translated_sid;
-  NTSTATUS status = LsaLookupNames(*policy, 1, &lsa_username, &translated_domains, &translated_sid);
-#ifndef UNICODE
-  HeapFree(GetProcessHeap(), 0, lsa_username.Buffer);
-#endif
-  HeapFree(GetProcessHeap(), 0, expanded);
-  if (policy == &handle) LsaClose(handle);
-  if (status) {
-    LsaFreeMemory(translated_domains);
-    LsaFreeMemory(translated_sid);
-    print_message(stderr, NSSM_MESSAGE_LSALOOKUPNAMES_FAILED, username, error_string(LsaNtStatusToWinError(status)));
-    return 5;
-  }
-
-  if (translated_sid->Use != SidTypeUser && translated_sid->Use != SidTypeWellKnownGroup) {
-    LsaFreeMemory(translated_domains);
-    LsaFreeMemory(translated_sid);
-    print_message(stderr, NSSM_GUI_INVALID_USERNAME, username);
-    return 6;
-  }
-
-  LSA_TRUST_INFORMATION *trust = &translated_domains->Domains[translated_sid->DomainIndex];
-  if (! trust || ! IsValidSid(trust->Sid)) {
-    LsaFreeMemory(translated_domains);
-    LsaFreeMemory(translated_sid);
-    print_message(stderr, NSSM_GUI_INVALID_USERNAME, username);
-    return 7;
-  }
-
-  /* GetSidSubAuthority*() return pointers! */
-  unsigned char *n = GetSidSubAuthorityCount(trust->Sid);
-
-  /* Convert translated SID to SID. */
-  *sid = (SID *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, GetSidLengthRequired(*n + 1));
-  if (! *sid) {
-    LsaFreeMemory(translated_domains);
-    LsaFreeMemory(translated_sid);
-    print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SID"), _T("username_sid"));
-    return 8;
-  }
-
-  unsigned long error;
-  if (! InitializeSid(*sid, GetSidIdentifierAuthority(trust->Sid), *n + 1)) {
-    error = GetLastError();
-    HeapFree(GetProcessHeap(), 0, *sid);
-    LsaFreeMemory(translated_domains);
-    LsaFreeMemory(translated_sid);
-    print_message(stderr, NSSM_MESSAGE_INITIALIZESID_FAILED, username, error_string(error));
-    return 9;
-  }
-
-  for (unsigned char i = 0; i <= *n; i++) {
-    unsigned long *sub = GetSidSubAuthority(*sid, i);
-    if (i < *n) *sub = *GetSidSubAuthority(trust->Sid, i);
-    else *sub = translated_sid->RelativeId;
-  }
-
-  int ret = 0;
-  if (translated_sid->Use == SidTypeWellKnownGroup && ! well_known_sid(*sid)) {
-    print_message(stderr, NSSM_GUI_INVALID_USERNAME, username);
-    ret = 10;
-  }
-
-  LsaFreeMemory(translated_domains);
-  LsaFreeMemory(translated_sid);
-
-  return ret;
-}
-
-int username_sid(const TCHAR *username, SID **sid) {
-  return username_sid(username, sid, 0);
-}
-
-int canonicalise_username(const TCHAR *username, TCHAR **canon) {
-  LSA_HANDLE policy;
-  if (open_lsa_policy(&policy)) return 1;
-
-  SID *sid;
-  if (username_sid(username, &sid, &policy)) return 2;
-  PSID sids = { sid };
-
-  LSA_REFERENCED_DOMAIN_LIST *translated_domains;
-  LSA_TRANSLATED_NAME *translated_name;
-  NTSTATUS status = LsaLookupSids(policy, 1, &sids, &translated_domains, &translated_name);
-  if (status) {
-    LsaFreeMemory(translated_domains);
-    LsaFreeMemory(translated_name);
-    print_message(stderr, NSSM_MESSAGE_LSALOOKUPSIDS_FAILED, error_string(LsaNtStatusToWinError(status)));
-    return 3;
-  }
-
-  LSA_TRUST_INFORMATION *trust = &translated_domains->Domains[translated_name->DomainIndex];
-  LSA_UNICODE_STRING lsa_canon;
-  lsa_canon.Length = translated_name->Name.Length + trust->Name.Length + sizeof(wchar_t);
-  lsa_canon.MaximumLength = lsa_canon.Length + sizeof(wchar_t);
-  lsa_canon.Buffer = (wchar_t *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lsa_canon.MaximumLength);
-  if (! lsa_canon.Buffer) {
-    LsaFreeMemory(translated_domains);
-    LsaFreeMemory(translated_name);
-    print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("lsa_canon"), _T("username_sid"));
-    return 9;
-  }
-
-  /* Buffer is wchar_t but Length is in bytes. */
-  memmove((char *) lsa_canon.Buffer, trust->Name.Buffer, trust->Name.Length);
-  memmove((char *) lsa_canon.Buffer + trust->Name.Length, L"\\", sizeof(wchar_t));
-  memmove((char *) lsa_canon.Buffer + trust->Name.Length + sizeof(wchar_t), translated_name->Name.Buffer, translated_name->Name.Length);
-
-#ifdef UNICODE
-  *canon = lsa_canon.Buffer;
-#else
-  size_t buflen;
-  wcstombs_s(&buflen, NULL, 0, lsa_canon.Buffer, _TRUNCATE);
-  *canon = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, buflen);
-  if (! *canon) {
-    LsaFreeMemory(translated_domains);
-    LsaFreeMemory(translated_name);
-    print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canon"), _T("username_sid"));
-    return 10;
-  }
-  wcstombs_s(&buflen, *canon, buflen, lsa_canon.Buffer, _TRUNCATE);
-  HeapFree(GetProcessHeap(), 0, lsa_canon.Buffer);
-#endif
-
-  LsaFreeMemory(translated_domains);
-  LsaFreeMemory(translated_name);
-
-  return 0;
-}
-
-/* Do two usernames map to the same SID? */
-int username_equiv(const TCHAR *a, const TCHAR *b) {
-  SID *sid_a, *sid_b;
-  if (username_sid(a, &sid_a)) return 0;
-
-  if (username_sid(b, &sid_b)) {
-    FreeSid(sid_a);
-    return 0;
-  }
-
-  int ret = 0;
-  if (EqualSid(sid_a, sid_b)) ret = 1;
-
-  FreeSid(sid_a);
-  FreeSid(sid_b);
-
-  return ret;
-}
-
-/* Does the username represent the LocalSystem account? */
-int is_localsystem(const TCHAR *username) {
-  if (str_equiv(username, NSSM_LOCALSYSTEM_ACCOUNT)) return 1;
-  if (! imports.IsWellKnownSid) return 0;
-
-  SID *sid;
-  if (username_sid(username, &sid)) return 0;
-
-  int ret = 0;
-  if (imports.IsWellKnownSid(sid, WinLocalSystemSid)) ret = 1;
-
-  FreeSid(sid);
-
-  return ret;
-}
-
-/*
-  Get well-known alias for LocalSystem and friends.
-  Returns a pointer to a static string.  DO NOT try to free it.
-*/
-const TCHAR *well_known_sid(SID *sid) {
-  if (! imports.IsWellKnownSid) return 0;
-  if (imports.IsWellKnownSid(sid, WinLocalSystemSid)) return NSSM_LOCALSYSTEM_ACCOUNT;
-  if (imports.IsWellKnownSid(sid, WinLocalServiceSid)) return NSSM_LOCALSERVICE_ACCOUNT;
-  if (imports.IsWellKnownSid(sid, WinNetworkServiceSid)) return NSSM_NETWORKSERVICE_ACCOUNT;
-  return 0;
-}
-
-const TCHAR *well_known_username(const TCHAR *username) {
-  if (! username) return NSSM_LOCALSYSTEM_ACCOUNT;
-  if (str_equiv(username, NSSM_LOCALSYSTEM_ACCOUNT)) return NSSM_LOCALSYSTEM_ACCOUNT;
-  SID *sid;
-  if (username_sid(username, &sid)) return 0;
-
-  const TCHAR *well_known = well_known_sid(sid);
-  FreeSid(sid);
-
-  return well_known;
-}
-
-int grant_logon_as_service(const TCHAR *username) {
-  if (! username) return 0;
-
-  /* Open Policy object. */
-  LSA_OBJECT_ATTRIBUTES attributes;
-  ZeroMemory(&attributes, sizeof(attributes));
-
-  LSA_HANDLE policy;
-  NTSTATUS status;
-
-  if (open_lsa_policy(&policy)) return 1;
-
-  /* Look up SID for the account. */
-  SID *sid;
-  if (username_sid(username, &sid, &policy)) {
-    LsaClose(policy);
-    return 2;
-  }
-
-  /*
-    Shouldn't happen because it should have been checked before callling this function.
-  */
-  if (well_known_sid(sid)) {
-    LsaClose(policy);
-    return 3;
-  }
-
-  /* Check if the SID has the "Log on as a service" right. */
-  LSA_UNICODE_STRING lsa_right;
-  lsa_right.Buffer = NSSM_LOGON_AS_SERVICE_RIGHT;
-  lsa_right.Length = (unsigned short) wcslen(lsa_right.Buffer) * sizeof(wchar_t);
-  lsa_right.MaximumLength = lsa_right.Length + sizeof(wchar_t);
-
-  LSA_UNICODE_STRING *rights;
-  unsigned long count = ~0;
-  status = LsaEnumerateAccountRights(policy, sid, &rights, &count);
-  if (status) {
-    /*
-      If the account has no rights set LsaEnumerateAccountRights() will return
-      STATUS_OBJECT_NAME_NOT_FOUND and set count to 0.
-    */
-    unsigned long error = LsaNtStatusToWinError(status);
-    if (error != ERROR_FILE_NOT_FOUND) {
-      FreeSid(sid);
-      LsaClose(policy);
-      print_message(stderr, NSSM_MESSAGE_LSAENUMERATEACCOUNTRIGHTS_FAILED, username, error_string(error));
-      return 4;
-    }
-  }
-
-  for (unsigned long i = 0; i < count; i++) {
-    if (rights[i].Length != lsa_right.Length) continue;
-    if (_wcsnicmp(rights[i].Buffer, lsa_right.Buffer, lsa_right.MaximumLength)) continue;
-    /* The SID has the right. */
-    FreeSid(sid);
-    LsaFreeMemory(rights);
-    LsaClose(policy);
-    return 0;
-  }
-  LsaFreeMemory(rights);
-
-  /* Add the right. */
-  status = LsaAddAccountRights(policy, sid, &lsa_right, 1);
-  FreeSid(sid);
-  LsaClose(policy);
-  if (status) {
-    print_message(stderr, NSSM_MESSAGE_LSAADDACCOUNTRIGHTS_FAILED, error_string(LsaNtStatusToWinError(status)));
-    return 5;
-  }
-
-  print_message(stdout, NSSM_MESSAGE_GRANTED_LOGON_AS_SERVICE, username);
-  return 0;
-}
+#include "nssm.h"\r
+\r
+#include <sddl.h>\r
+\r
+extern imports_t imports;\r
+\r
+/* Open Policy object. */\r
+int open_lsa_policy(LSA_HANDLE *policy) {\r
+  LSA_OBJECT_ATTRIBUTES attributes;\r
+  ZeroMemory(&attributes, sizeof(attributes));\r
+\r
+  NTSTATUS status = LsaOpenPolicy(0, &attributes, POLICY_ALL_ACCESS, policy);\r
+  if (status) {\r
+    print_message(stderr, NSSM_MESSAGE_LSAOPENPOLICY_FAILED, error_string(LsaNtStatusToWinError(status)));\r
+    return 1;\r
+  }\r
+\r
+  return 0;\r
+}\r
+\r
+/* Look up SID for an account. */\r
+int username_sid(const TCHAR *username, SID **sid, LSA_HANDLE *policy) {\r
+  LSA_HANDLE handle;\r
+  if (! policy) {\r
+    policy = &handle;\r
+    if (open_lsa_policy(policy)) return 1;\r
+  }\r
+\r
+  /*\r
+    LsaLookupNames() can't look up .\username but can look up\r
+    %COMPUTERNAME%\username.  ChangeServiceConfig() writes .\username to the\r
+    registry when %COMPUTERNAME%\username is a passed as a parameter.  We\r
+    need to preserve .\username when calling ChangeServiceConfig() without\r
+    changing the username, but expand to %COMPUTERNAME%\username when calling\r
+    LsaLookupNames().\r
+  */\r
+  TCHAR *expanded;\r
+  unsigned long expandedlen;\r
+  if (_tcsnicmp(_T(".\\"), username, 2)) {\r
+    expandedlen = (unsigned long) (_tcslen(username) + 1) * sizeof(TCHAR);\r
+    expanded = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, expandedlen);\r
+    if (! expanded) {\r
+      print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("expanded"), _T("username_sid"));\r
+      if (policy == &handle) LsaClose(handle);\r
+      return 2;\r
+    }\r
+    memmove(expanded, username, expandedlen);\r
+  }\r
+  else {\r
+    TCHAR computername[MAX_COMPUTERNAME_LENGTH + 1];\r
+    expandedlen = _countof(computername);\r
+    GetComputerName(computername, &expandedlen);\r
+    expandedlen += (unsigned long) _tcslen(username);\r
+\r
+    expanded = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, expandedlen * sizeof(TCHAR));\r
+    if (! expanded) {\r
+      print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("expanded"), _T("username_sid"));\r
+      if (policy == &handle) LsaClose(handle);\r
+      return 2;\r
+    }\r
+    _sntprintf_s(expanded, expandedlen, _TRUNCATE, _T("%s\\%s"), computername, username + 2);\r
+  }\r
+\r
+  LSA_UNICODE_STRING lsa_username;\r
+#ifdef UNICODE\r
+  lsa_username.Buffer = (wchar_t *) expanded;\r
+  lsa_username.Length = (unsigned short) _tcslen(expanded) * sizeof(TCHAR);\r
+  lsa_username.MaximumLength = lsa_username.Length + sizeof(TCHAR);\r
+#else\r
+  size_t buflen;\r
+  mbstowcs_s(&buflen, NULL, 0, expanded, _TRUNCATE);\r
+  lsa_username.MaximumLength = (unsigned short) buflen * sizeof(wchar_t);\r
+  lsa_username.Length = lsa_username.MaximumLength - sizeof(wchar_t);\r
+  lsa_username.Buffer = (wchar_t *) HeapAlloc(GetProcessHeap(), 0, lsa_username.MaximumLength);\r
+  if (lsa_username.Buffer) mbstowcs_s(&buflen, lsa_username.Buffer, lsa_username.MaximumLength, expanded, _TRUNCATE);\r
+  else {\r
+    if (policy == &handle) LsaClose(handle);\r
+    HeapFree(GetProcessHeap(), 0, expanded);\r
+    print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("LSA_UNICODE_STRING"), _T("username_sid()"));\r
+    return 4;\r
+  }\r
+#endif\r
+\r
+  LSA_REFERENCED_DOMAIN_LIST *translated_domains;\r
+  LSA_TRANSLATED_SID *translated_sid;\r
+  NTSTATUS status = LsaLookupNames(*policy, 1, &lsa_username, &translated_domains, &translated_sid);\r
+#ifndef UNICODE\r
+  HeapFree(GetProcessHeap(), 0, lsa_username.Buffer);\r
+#endif\r
+  HeapFree(GetProcessHeap(), 0, expanded);\r
+  if (policy == &handle) LsaClose(handle);\r
+  if (status) {\r
+    LsaFreeMemory(translated_domains);\r
+    LsaFreeMemory(translated_sid);\r
+    print_message(stderr, NSSM_MESSAGE_LSALOOKUPNAMES_FAILED, username, error_string(LsaNtStatusToWinError(status)));\r
+    return 5;\r
+  }\r
+\r
+  if (translated_sid->Use != SidTypeUser && translated_sid->Use != SidTypeWellKnownGroup) {\r
+    LsaFreeMemory(translated_domains);\r
+    LsaFreeMemory(translated_sid);\r
+    print_message(stderr, NSSM_GUI_INVALID_USERNAME, username);\r
+    return 6;\r
+  }\r
+\r
+  LSA_TRUST_INFORMATION *trust = &translated_domains->Domains[translated_sid->DomainIndex];\r
+  if (! trust || ! IsValidSid(trust->Sid)) {\r
+    LsaFreeMemory(translated_domains);\r
+    LsaFreeMemory(translated_sid);\r
+    print_message(stderr, NSSM_GUI_INVALID_USERNAME, username);\r
+    return 7;\r
+  }\r
+\r
+  /* GetSidSubAuthority*() return pointers! */\r
+  unsigned char *n = GetSidSubAuthorityCount(trust->Sid);\r
+\r
+  /* Convert translated SID to SID. */\r
+  *sid = (SID *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, GetSidLengthRequired(*n + 1));\r
+  if (! *sid) {\r
+    LsaFreeMemory(translated_domains);\r
+    LsaFreeMemory(translated_sid);\r
+    print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SID"), _T("username_sid"));\r
+    return 8;\r
+  }\r
+\r
+  unsigned long error;\r
+  if (! InitializeSid(*sid, GetSidIdentifierAuthority(trust->Sid), *n + 1)) {\r
+    error = GetLastError();\r
+    HeapFree(GetProcessHeap(), 0, *sid);\r
+    LsaFreeMemory(translated_domains);\r
+    LsaFreeMemory(translated_sid);\r
+    print_message(stderr, NSSM_MESSAGE_INITIALIZESID_FAILED, username, error_string(error));\r
+    return 9;\r
+  }\r
+\r
+  for (unsigned char i = 0; i <= *n; i++) {\r
+    unsigned long *sub = GetSidSubAuthority(*sid, i);\r
+    if (i < *n) *sub = *GetSidSubAuthority(trust->Sid, i);\r
+    else *sub = translated_sid->RelativeId;\r
+  }\r
+\r
+  int ret = 0;\r
+  if (translated_sid->Use == SidTypeWellKnownGroup && ! well_known_sid(*sid)) {\r
+    print_message(stderr, NSSM_GUI_INVALID_USERNAME, username);\r
+    ret = 10;\r
+  }\r
+\r
+  LsaFreeMemory(translated_domains);\r
+  LsaFreeMemory(translated_sid);\r
+\r
+  return ret;\r
+}\r
+\r
+int username_sid(const TCHAR *username, SID **sid) {\r
+  return username_sid(username, sid, 0);\r
+}\r
+\r
+int canonicalise_username(const TCHAR *username, TCHAR **canon) {\r
+  LSA_HANDLE policy;\r
+  if (open_lsa_policy(&policy)) return 1;\r
+\r
+  SID *sid;\r
+  if (username_sid(username, &sid, &policy)) return 2;\r
+  PSID sids = { sid };\r
+\r
+  LSA_REFERENCED_DOMAIN_LIST *translated_domains;\r
+  LSA_TRANSLATED_NAME *translated_name;\r
+  NTSTATUS status = LsaLookupSids(policy, 1, &sids, &translated_domains, &translated_name);\r
+  if (status) {\r
+    LsaFreeMemory(translated_domains);\r
+    LsaFreeMemory(translated_name);\r
+    print_message(stderr, NSSM_MESSAGE_LSALOOKUPSIDS_FAILED, error_string(LsaNtStatusToWinError(status)));\r
+    return 3;\r
+  }\r
+\r
+  LSA_TRUST_INFORMATION *trust = &translated_domains->Domains[translated_name->DomainIndex];\r
+  LSA_UNICODE_STRING lsa_canon;\r
+  lsa_canon.Length = translated_name->Name.Length + trust->Name.Length + sizeof(wchar_t);\r
+  lsa_canon.MaximumLength = lsa_canon.Length + sizeof(wchar_t);\r
+  lsa_canon.Buffer = (wchar_t *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lsa_canon.MaximumLength);\r
+  if (! lsa_canon.Buffer) {\r
+    LsaFreeMemory(translated_domains);\r
+    LsaFreeMemory(translated_name);\r
+    print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("lsa_canon"), _T("username_sid"));\r
+    return 9;\r
+  }\r
+\r
+  /* Buffer is wchar_t but Length is in bytes. */\r
+  memmove((char *) lsa_canon.Buffer, trust->Name.Buffer, trust->Name.Length);\r
+  memmove((char *) lsa_canon.Buffer + trust->Name.Length, L"\\", sizeof(wchar_t));\r
+  memmove((char *) lsa_canon.Buffer + trust->Name.Length + sizeof(wchar_t), translated_name->Name.Buffer, translated_name->Name.Length);\r
+\r
+#ifdef UNICODE\r
+  *canon = lsa_canon.Buffer;\r
+#else\r
+  size_t buflen;\r
+  wcstombs_s(&buflen, NULL, 0, lsa_canon.Buffer, _TRUNCATE);\r
+  *canon = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, buflen);\r
+  if (! *canon) {\r
+    LsaFreeMemory(translated_domains);\r
+    LsaFreeMemory(translated_name);\r
+    print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canon"), _T("username_sid"));\r
+    return 10;\r
+  }\r
+  wcstombs_s(&buflen, *canon, buflen, lsa_canon.Buffer, _TRUNCATE);\r
+  HeapFree(GetProcessHeap(), 0, lsa_canon.Buffer);\r
+#endif\r
+\r
+  LsaFreeMemory(translated_domains);\r
+  LsaFreeMemory(translated_name);\r
+\r
+  return 0;\r
+}\r
+\r
+/* Do two usernames map to the same SID? */\r
+int username_equiv(const TCHAR *a, const TCHAR *b) {\r
+  SID *sid_a, *sid_b;\r
+  if (username_sid(a, &sid_a)) return 0;\r
+\r
+  if (username_sid(b, &sid_b)) {\r
+    FreeSid(sid_a);\r
+    return 0;\r
+  }\r
+\r
+  int ret = 0;\r
+  if (EqualSid(sid_a, sid_b)) ret = 1;\r
+\r
+  FreeSid(sid_a);\r
+  FreeSid(sid_b);\r
+\r
+  return ret;\r
+}\r
+\r
+/* Does the username represent the LocalSystem account? */\r
+int is_localsystem(const TCHAR *username) {\r
+  if (str_equiv(username, NSSM_LOCALSYSTEM_ACCOUNT)) return 1;\r
+  if (! imports.IsWellKnownSid) return 0;\r
+\r
+  SID *sid;\r
+  if (username_sid(username, &sid)) return 0;\r
+\r
+  int ret = 0;\r
+  if (imports.IsWellKnownSid(sid, WinLocalSystemSid)) ret = 1;\r
+\r
+  FreeSid(sid);\r
+\r
+  return ret;\r
+}\r
+\r
+/*\r
+  Get well-known alias for LocalSystem and friends.\r
+  Returns a pointer to a static string.  DO NOT try to free it.\r
+*/\r
+const TCHAR *well_known_sid(SID *sid) {\r
+  if (! imports.IsWellKnownSid) return 0;\r
+  if (imports.IsWellKnownSid(sid, WinLocalSystemSid)) return NSSM_LOCALSYSTEM_ACCOUNT;\r
+  if (imports.IsWellKnownSid(sid, WinLocalServiceSid)) return NSSM_LOCALSERVICE_ACCOUNT;\r
+  if (imports.IsWellKnownSid(sid, WinNetworkServiceSid)) return NSSM_NETWORKSERVICE_ACCOUNT;\r
+  return 0;\r
+}\r
+\r
+const TCHAR *well_known_username(const TCHAR *username) {\r
+  if (! username) return NSSM_LOCALSYSTEM_ACCOUNT;\r
+  if (str_equiv(username, NSSM_LOCALSYSTEM_ACCOUNT)) return NSSM_LOCALSYSTEM_ACCOUNT;\r
+  SID *sid;\r
+  if (username_sid(username, &sid)) return 0;\r
+\r
+  const TCHAR *well_known = well_known_sid(sid);\r
+  FreeSid(sid);\r
+\r
+  return well_known;\r
+}\r
+\r
+int grant_logon_as_service(const TCHAR *username) {\r
+  if (! username) return 0;\r
+\r
+  /* Open Policy object. */\r
+  LSA_OBJECT_ATTRIBUTES attributes;\r
+  ZeroMemory(&attributes, sizeof(attributes));\r
+\r
+  LSA_HANDLE policy;\r
+  NTSTATUS status;\r
+\r
+  if (open_lsa_policy(&policy)) return 1;\r
+\r
+  /* Look up SID for the account. */\r
+  SID *sid;\r
+  if (username_sid(username, &sid, &policy)) {\r
+    LsaClose(policy);\r
+    return 2;\r
+  }\r
+\r
+  /*\r
+    Shouldn't happen because it should have been checked before callling this function.\r
+  */\r
+  if (well_known_sid(sid)) {\r
+    LsaClose(policy);\r
+    return 3;\r
+  }\r
+\r
+  /* Check if the SID has the "Log on as a service" right. */\r
+  LSA_UNICODE_STRING lsa_right;\r
+  lsa_right.Buffer = NSSM_LOGON_AS_SERVICE_RIGHT;\r
+  lsa_right.Length = (unsigned short) wcslen(lsa_right.Buffer) * sizeof(wchar_t);\r
+  lsa_right.MaximumLength = lsa_right.Length + sizeof(wchar_t);\r
+\r
+  LSA_UNICODE_STRING *rights;\r
+  unsigned long count = ~0;\r
+  status = LsaEnumerateAccountRights(policy, sid, &rights, &count);\r
+  if (status) {\r
+    /*\r
+      If the account has no rights set LsaEnumerateAccountRights() will return\r
+      STATUS_OBJECT_NAME_NOT_FOUND and set count to 0.\r
+    */\r
+    unsigned long error = LsaNtStatusToWinError(status);\r
+    if (error != ERROR_FILE_NOT_FOUND) {\r
+      FreeSid(sid);\r
+      LsaClose(policy);\r
+      print_message(stderr, NSSM_MESSAGE_LSAENUMERATEACCOUNTRIGHTS_FAILED, username, error_string(error));\r
+      return 4;\r
+    }\r
+  }\r
+\r
+  for (unsigned long i = 0; i < count; i++) {\r
+    if (rights[i].Length != lsa_right.Length) continue;\r
+    if (_wcsnicmp(rights[i].Buffer, lsa_right.Buffer, lsa_right.MaximumLength)) continue;\r
+    /* The SID has the right. */\r
+    FreeSid(sid);\r
+    LsaFreeMemory(rights);\r
+    LsaClose(policy);\r
+    return 0;\r
+  }\r
+  LsaFreeMemory(rights);\r
+\r
+  /* Add the right. */\r
+  status = LsaAddAccountRights(policy, sid, &lsa_right, 1);\r
+  FreeSid(sid);\r
+  LsaClose(policy);\r
+  if (status) {\r
+    print_message(stderr, NSSM_MESSAGE_LSAADDACCOUNTRIGHTS_FAILED, error_string(LsaNtStatusToWinError(status)));\r
+    return 5;\r
+  }\r
+\r
+  print_message(stdout, NSSM_MESSAGE_GRANTED_LOGON_AS_SERVICE, username);\r
+  return 0;\r
+}\r
index e6b40f0..232e1f9 100644 (file)
--- a/account.h
+++ b/account.h
@@ -1,24 +1,24 @@
-#ifndef ACCOUNT_H
-#define ACCOUNT_H
-
-#include <ntsecapi.h>
-
-/* Not really an account.  The canonical name is NT Authority\System. */
-#define NSSM_LOCALSYSTEM_ACCOUNT _T("LocalSystem")
-/* Other well-known accounts which can start a service without a password. */
-#define NSSM_LOCALSERVICE_ACCOUNT _T("NT Authority\\LocalService")
-#define NSSM_NETWORKSERVICE_ACCOUNT _T("NT Authority\\NetworkService")
-/* This is explicitly a wide string. */
-#define NSSM_LOGON_AS_SERVICE_RIGHT L"SeServiceLogonRight"
-
-int open_lsa_policy(LSA_HANDLE *);
-int username_sid(const TCHAR *, SID **, LSA_HANDLE *);
-int username_sid(const TCHAR *, SID **);
-int username_equiv(const TCHAR *, const TCHAR *);
-int canonicalise_username(const TCHAR *, TCHAR **);
-int is_localsystem(const TCHAR *);
-const TCHAR *well_known_sid(SID *);
-const TCHAR *well_known_username(const TCHAR *);
-int grant_logon_as_service(const TCHAR *);
-
-#endif
+#ifndef ACCOUNT_H\r
+#define ACCOUNT_H\r
+\r
+#include <ntsecapi.h>\r
+\r
+/* Not really an account.  The canonical name is NT Authority\System. */\r
+#define NSSM_LOCALSYSTEM_ACCOUNT _T("LocalSystem")\r
+/* Other well-known accounts which can start a service without a password. */\r
+#define NSSM_LOCALSERVICE_ACCOUNT _T("NT Authority\\LocalService")\r
+#define NSSM_NETWORKSERVICE_ACCOUNT _T("NT Authority\\NetworkService")\r
+/* This is explicitly a wide string. */\r
+#define NSSM_LOGON_AS_SERVICE_RIGHT L"SeServiceLogonRight"\r
+\r
+int open_lsa_policy(LSA_HANDLE *);\r
+int username_sid(const TCHAR *, SID **, LSA_HANDLE *);\r
+int username_sid(const TCHAR *, SID **);\r
+int username_equiv(const TCHAR *, const TCHAR *);\r
+int canonicalise_username(const TCHAR *, TCHAR **);\r
+int is_localsystem(const TCHAR *);\r
+const TCHAR *well_known_sid(SID *);\r
+const TCHAR *well_known_username(const TCHAR *);\r
+int grant_logon_as_service(const TCHAR *);\r
+\r
+#endif\r
index 66c99ea..9a97729 100644 (file)
-#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++;
-}
+#include "nssm.h"\r
+\r
+/* See if we were launched from a console window. */\r
+void check_console() {\r
+  /* If we're running in a service context there will be no console window. */\r
+  HWND console = GetConsoleWindow();\r
+  if (! console) return;\r
+\r
+  unsigned long pid;\r
+  if (! GetWindowThreadProcessId(console, &pid)) return;\r
+\r
+  /*\r
+    If the process associated with the console window handle is the same as\r
+    this process, we were not launched from an existing console.  The user\r
+    probably double-clicked our executable.\r
+  */\r
+  if (GetCurrentProcessId() != pid) return;\r
+\r
+  /* We close our new console so that subsequent messages appear in a popup. */\r
+  FreeConsole();\r
+}\r
+\r
+/* Helpers for drawing the banner. */\r
+static inline void block(unsigned int a, short x, short y, unsigned long n) {\r
+  HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);\r
+  TCHAR s = _T(' ');\r
+\r
+  unsigned long out;\r
+  COORD c = { x, y };\r
+  FillConsoleOutputAttribute(h, a, n, c, &out);\r
+  FillConsoleOutputCharacter(h, s, n, c, &out);\r
+}\r
+\r
+static inline void R(short x, short y, unsigned long n) {\r
+  block(BACKGROUND_RED | BACKGROUND_INTENSITY, x, y, n);\r
+}\r
+\r
+static inline void r(short x, short y, unsigned long n) {\r
+  block(BACKGROUND_RED, x, y, n);\r
+}\r
+\r
+static inline void b(short x, short y, unsigned long n) {\r
+  block(0, x, y, n);\r
+}\r
+\r
+void alloc_console(nssm_service_t *service) {\r
+  if (service->no_console) return;\r
+\r
+  AllocConsole();\r
+\r
+  /* Disable accidental closure. */\r
+  HWND window = GetConsoleWindow();\r
+  HMENU menu = GetSystemMenu(window, false);\r
+  EnableMenuItem(menu, SC_CLOSE, MF_GRAYED);\r
+\r
+  /* Set a title like "[NSSM] Jenkins" */\r
+  TCHAR displayname[SERVICE_NAME_LENGTH];\r
+  unsigned long len = _countof(displayname);\r
+  SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT);\r
+  if (services) {\r
+    if (! GetServiceDisplayName(services, service->name, displayname, &len)) ZeroMemory(displayname, sizeof(displayname));\r
+    CloseServiceHandle(services);\r
+  }\r
+  if (! displayname[0]) _sntprintf_s(displayname, _countof(displayname), _TRUNCATE, _T("%s"), service->name);\r
+\r
+  TCHAR title[65535];\r
+  _sntprintf_s(title, _countof(title), _TRUNCATE, _T("[%s] %s"), NSSM, displayname);\r
+  SetConsoleTitle(title);\r
+\r
+  /* Draw the NSSM logo on the console window. */\r
+  short y = 0;\r
+\r
+  b(0, y, 80);\r
+  y++;\r
+\r
+  b(0, y, 80);\r
+  y++;\r
+\r
+  b(0, y, 80);\r
+  y++;\r
+\r
+  b(0, y, 80);\r
+  y++;\r
+\r
+  b(0, y, 80);\r
+  r(18, y, 5); r(28, y, 4); r(41, y, 4); r(68, y, 1);\r
+  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);\r
+  y++;\r
+\r
+  b(0, y, 80);\r
+  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
+  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);\r
+  y++;\r
+\r
+  b(0, y, 80);\r
+  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
+  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);\r
+  y++;\r
+\r
+  b(0, y, 80);\r
+  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
+  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);\r
+  y++;\r
+\r
+  b(0, y, 80);\r
+  r(9, y, 1); r(56, y, 1); r(66, y, 2);\r
+  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);\r
+  y++;\r
+\r
+  b(0, y, 80);\r
+  r(9, y, 1); r(26, y, 1); r(39, y, 1); r(59, y, 1);\r
+  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);\r
+  y++;\r
+\r
+  b(0, y, 80);\r
+  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
+  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);\r
+  y++;\r
+\r
+  b(0, y, 80);\r
+  r(9, y, 1); r(13, y, 4); r(27, y, 7); r(40, y, 7);\r
+  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);\r
+  y++;\r
+\r
+  b(0, y, 80);\r
+  r(9, y, 1); r(60, y, 1); r(65, y, 1);\r
+  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);\r
+  y++;\r
+\r
+  b(0, y, 80);\r
+  r(9, y, 1); r(31, y, 1); r(44, y, 1); r(58, y, 1); r(64, y, 1);\r
+  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);\r
+  y++;\r
+\r
+  b(0, y, 80);\r
+  r(9, y, 1); r(33, y, 1); r(46, y, 1); r(61, y, 1); r(64, y, 1);\r
+  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);\r
+  y++;\r
+\r
+  b(0, y, 80);\r
+  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
+  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);\r
+  y++;\r
+\r
+  b(0, y, 80);\r
+  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
+  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);\r
+  y++;\r
+\r
+  b(0, y, 80);\r
+  r(26, y, 2); r(39, y, 2); r(63, y, 1);\r
+  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);\r
+  y++;\r
+\r
+  b(0, y, 80);\r
+  r(34, y, 1); r(47, y, 1); r(60, y, 1);\r
+  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);\r
+  y++;\r
+\r
+  b(0, y, 80);\r
+  r(8, y, 1); r(35, y, 1); r(48, y, 1); r(62, y, 1); r(71, y, 1);\r
+  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);\r
+  y++;\r
+\r
+  b(0, y, 80);\r
+  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
+  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);\r
+  y++;\r
+\r
+  b(0, y, 80);\r
+  y++;\r
+\r
+  b(0, y, 80);\r
+  y++;\r
+}\r
index 8aa8966..cbccd3c 100644 (file)
--- a/console.h
+++ b/console.h
@@ -1,7 +1,7 @@
-#ifndef CONSOLE_H
-#define CONSOLE_H
-
-void check_console();
-void alloc_console(nssm_service_t *);
-
-#endif
+#ifndef CONSOLE_H\r
+#define CONSOLE_H\r
+\r
+void check_console();\r
+void alloc_console(nssm_service_t *);\r
+\r
+#endif\r
diff --git a/env.cpp b/env.cpp
index a1ca900..4427d91 100644 (file)
--- a/env.cpp
+++ b/env.cpp
-#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 = (TCHAR *) nssm_imagepath();
-  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);
-}
-
-/* Safely get a copy of the current environment. */
-TCHAR *copy_environment() {
-  TCHAR *rawenv = GetEnvironmentStrings();
-  if (! rawenv) return NULL;
-  TCHAR *env = copy_environment_block(rawenv);
-  FreeEnvironmentStrings(rawenv);
-  return env;
-}
+#include "nssm.h"\r
+\r
+/* Copy an environment block. */\r
+TCHAR *copy_environment_block(TCHAR *env) {\r
+  unsigned long len;\r
+\r
+  if (! env) return 0;\r
+  for (len = 0; env[len]; len++) while (env[len]) len++;\r
+  if (! len++) return 0;\r
+\r
+  TCHAR *newenv = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR));\r
+  if (! newenv) {\r
+    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("environment"), _T("copy_environment_block()"), 0);\r
+    return 0;\r
+  }\r
+\r
+  memmove(newenv, env, len * sizeof(TCHAR));\r
+  return newenv;\r
+}\r
+\r
+/*\r
+  The environment block starts with variables of the form\r
+  =C:=C:\Windows\System32 which we ignore.\r
+*/\r
+TCHAR *useful_environment(TCHAR *rawenv) {\r
+  TCHAR *env = rawenv;\r
+\r
+  if (env) {\r
+    while (*env == _T('=')) {\r
+      for ( ; *env; env++);\r
+      env++;\r
+    }\r
+  }\r
+\r
+  return env;\r
+}\r
+\r
+/* Expand an environment variable.  Must call HeapFree() on the result. */\r
+TCHAR *expand_environment_string(TCHAR *string) {\r
+  unsigned long len;\r
+\r
+  len = ExpandEnvironmentStrings(string, 0, 0);\r
+  if (! len) {\r
+    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_EXPANDENVIRONMENTSTRINGS_FAILED, string, error_string(GetLastError()), 0);\r
+    return 0;\r
+  }\r
+\r
+  TCHAR *ret = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR));\r
+  if (! ret) {\r
+    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("ExpandEnvironmentStrings()"), _T("expand_environment_string"), 0);\r
+    return 0;\r
+  }\r
+\r
+  if (! ExpandEnvironmentStrings(string, ret, len)) {\r
+    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_EXPANDENVIRONMENTSTRINGS_FAILED, string, error_string(GetLastError()), 0);\r
+    HeapFree(GetProcessHeap(), 0, ret);\r
+    return 0;\r
+  }\r
+\r
+  return ret;\r
+}\r
+\r
+/*\r
+  Set all the environment variables from an environment block in the current\r
+  environment or remove all the variables in the block from the current\r
+  environment.\r
+*/\r
+static int set_environment_block(TCHAR *env, bool set) {\r
+  int ret = 0;\r
+\r
+  TCHAR *s, *t;\r
+  for (s = env; *s; s++) {\r
+    for (t = s; *t && *t != _T('='); t++);\r
+    if (*t == _T('=')) {\r
+      *t = _T('\0');\r
+      if (set) {\r
+        TCHAR *expanded = expand_environment_string(++t);\r
+        if (expanded) {\r
+          if (! SetEnvironmentVariable(s, expanded)) ret++;\r
+          HeapFree(GetProcessHeap(), 0, expanded);\r
+        }\r
+        else {\r
+          if (! SetEnvironmentVariable(s, t)) ret++;\r
+        }\r
+      }\r
+      else {\r
+        if (! SetEnvironmentVariable(s, NULL)) ret++;\r
+      }\r
+      for (t++; *t; t++);\r
+    }\r
+    s = t;\r
+  }\r
+\r
+  return ret;\r
+}\r
+\r
+int set_environment_block(TCHAR *env) {\r
+  return set_environment_block(env, true);\r
+}\r
+\r
+static int unset_environment_block(TCHAR *env) {\r
+  return set_environment_block(env, false);\r
+}\r
+\r
+/* Remove all variables from the process environment. */\r
+int clear_environment() {\r
+  TCHAR *rawenv = GetEnvironmentStrings();\r
+  TCHAR *env = useful_environment(rawenv);\r
+\r
+  int ret = unset_environment_block(env);\r
+\r
+  if (rawenv) FreeEnvironmentStrings(rawenv);\r
+\r
+  return ret;\r
+}\r
+\r
+/* Set the current environment to exactly duplicate an environment block. */\r
+int duplicate_environment(TCHAR *rawenv) {\r
+  int ret = clear_environment();\r
+  TCHAR *env = useful_environment(rawenv);\r
+  ret += set_environment_block(env);\r
+  return ret;\r
+}\r
+\r
+/*\r
+  Verify an environment block.\r
+  Returns:  1 if environment is invalid.\r
+            0 if environment is OK.\r
+           -1 on error.\r
+*/\r
+int test_environment(TCHAR *env) {\r
+  TCHAR *path = (TCHAR *) nssm_imagepath();\r
+  STARTUPINFO si;\r
+  ZeroMemory(&si, sizeof(si));\r
+  si.cb = sizeof(si);\r
+  PROCESS_INFORMATION pi;\r
+  ZeroMemory(&pi, sizeof(pi));\r
+  unsigned long flags = CREATE_SUSPENDED;\r
+#ifdef UNICODE\r
+  flags |= CREATE_UNICODE_ENVIRONMENT;\r
+#endif\r
+\r
+  /*\r
+    Try to relaunch ourselves but with the candidate environment set.\r
+    Assuming no solar flare activity, the only reason this would fail is if\r
+    the environment were invalid.\r
+  */\r
+  if (CreateProcess(0, path, 0, 0, 0, flags, env, 0, &si, &pi)) {\r
+    TerminateProcess(pi.hProcess, 0);\r
+  }\r
+  else {\r
+    unsigned long error = GetLastError();\r
+    if (error == ERROR_INVALID_PARAMETER) return 1;\r
+    else return -1;\r
+  }\r
+\r
+  return 0;\r
+}\r
+\r
+/*\r
+  Duplicate an environment block returned by GetEnvironmentStrings().\r
+  Since such a block is by definition readonly, and duplicate_environment()\r
+  modifies its inputs, this function takes a copy of the input and operates\r
+  on that.\r
+*/\r
+void duplicate_environment_strings(TCHAR *env) {\r
+  TCHAR *newenv = copy_environment_block(env);\r
+  if (! newenv) return;\r
+\r
+  duplicate_environment(newenv);\r
+  HeapFree(GetProcessHeap(), 0, newenv);\r
+}\r
+\r
+/* Safely get a copy of the current environment. */\r
+TCHAR *copy_environment() {\r
+  TCHAR *rawenv = GetEnvironmentStrings();\r
+  if (! rawenv) return NULL;\r
+  TCHAR *env = copy_environment_block(rawenv);\r
+  FreeEnvironmentStrings(rawenv);\r
+  return env;\r
+}\r
diff --git a/env.h b/env.h
index b690106..a56456d 100644 (file)
--- a/env.h
+++ b/env.h
@@ -1,14 +1,14 @@
-#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 *);
-TCHAR *copy_environment();
-
-#endif
+#ifndef ENV_H\r
+#define ENV_H\r
+\r
+TCHAR *copy_environment_block(TCHAR *);\r
+TCHAR *useful_environment(TCHAR *);\r
+TCHAR *expand_environment_string(TCHAR *);\r
+int set_environment_block(TCHAR *);\r
+int clear_environment();\r
+int duplicate_environment(TCHAR *);\r
+int test_environment(TCHAR *);\r
+void duplicate_environment_strings(TCHAR *);\r
+TCHAR *copy_environment();\r
+\r
+#endif\r
index 780590b..01e3a50 100644 (file)
--- a/hook.cpp
+++ b/hook.cpp
-#include "nssm.h"
-
-typedef struct {
-  TCHAR *name;
-  HANDLE process_handle;
-  unsigned long pid;
-  unsigned long deadline;
-  FILETIME creation_time;
-  kill_t k;
-} hook_t;
-
-static unsigned long WINAPI await_hook(void *arg) {
-  hook_t *hook = (hook_t *) arg;
-  if (! hook) return NSSM_HOOK_STATUS_ERROR;
-
-  int ret = 0;
-  if (WaitForSingleObject(hook->process_handle, hook->deadline) == WAIT_TIMEOUT) ret = NSSM_HOOK_STATUS_TIMEOUT;
-
-  /* Tidy up hook process tree. */
-  if (hook->name) hook->k.name = hook->name;
-  else hook->k.name = _T("hook");
-  hook->k.process_handle = hook->process_handle;
-  hook->k.pid = hook->pid;
-  hook->k.stop_method = ~0;
-  hook->k.kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
-  hook->k.kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
-  hook->k.kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
-  hook->k.creation_time = hook->creation_time;
-  GetSystemTimeAsFileTime(&hook->k.exit_time);
-  kill_process_tree(&hook->k, hook->pid);
-
-  if (ret) {
-    CloseHandle(hook->process_handle);
-    if (hook->name) HeapFree(GetProcessHeap(), 0, hook->name);
-    HeapFree(GetProcessHeap(), 0, hook);
-    return ret;
-  }
-
-  unsigned long exitcode;
-  GetExitCodeProcess(hook->process_handle, &exitcode);
-  CloseHandle(hook->process_handle);
-
-  if (hook->name) HeapFree(GetProcessHeap(), 0, hook->name);
-  HeapFree(GetProcessHeap(), 0, hook);
-
-  if (exitcode == NSSM_HOOK_STATUS_ABORT) return NSSM_HOOK_STATUS_ABORT;
-  if (exitcode) return NSSM_HOOK_STATUS_FAILED;
-
-  return NSSM_HOOK_STATUS_SUCCESS;
-}
-
-static void set_hook_runtime(TCHAR *v, FILETIME *start, FILETIME *now) {
-  if (start && now) {
-    ULARGE_INTEGER s;
-    s.LowPart = start->dwLowDateTime;
-    s.HighPart = start->dwHighDateTime;
-    if (s.QuadPart) {
-      ULARGE_INTEGER t;
-      t.LowPart = now->dwLowDateTime;
-      t.HighPart = now->dwHighDateTime;
-      if (t.QuadPart && t.QuadPart >= s.QuadPart) {
-        t.QuadPart -= s.QuadPart;
-        t.QuadPart /= 10000LL;
-        TCHAR number[16];
-        _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%llu"), t.QuadPart);
-        SetEnvironmentVariable(v, number);
-        return;
-      }
-    }
-  }
-  SetEnvironmentVariable(v, _T(""));
-}
-
-static void add_thread_handle(hook_thread_t *hook_threads, HANDLE thread_handle, TCHAR *name) {
-  if (! hook_threads) return;
-
-  int num_threads = hook_threads->num_threads + 1;
-  hook_thread_data_t *data = (hook_thread_data_t *) HeapAlloc(GetProcessHeap(), 0, num_threads * sizeof(hook_thread_data_t));
-  if (! data) {
-    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("hook_thread_t"), _T("add_thread_handle()"), 0);
-    return;
-  }
-
-  int i;
-  for (i = 0; i < hook_threads->num_threads; i++) memmove(&data[i], &hook_threads->data[i], sizeof(data[i]));
-  memmove(data[i].name, name, sizeof(data[i].name));
-  data[i].thread_handle = thread_handle;
-
-  if (hook_threads->data) HeapFree(GetProcessHeap(), 0, hook_threads->data);
-  hook_threads->data = data;
-  hook_threads->num_threads = num_threads;
-}
-
-bool valid_hook_name(const TCHAR *hook_event, const TCHAR *hook_action, bool quiet) {
-  bool valid_event = false;
-  bool valid_action = false;
-
-  /* Exit/Post */
-  if (str_equiv(hook_event, NSSM_HOOK_EVENT_EXIT)) {
-    if (str_equiv(hook_action, NSSM_HOOK_ACTION_POST)) return true;
-    if (quiet) return false;
-    print_message(stderr, NSSM_MESSAGE_INVALID_HOOK_ACTION, hook_event);
-    _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_ACTION_POST);
-    return false;
-  }
-
-  /* Power/{Change,Resume} */
-  if (str_equiv(hook_event, NSSM_HOOK_EVENT_POWER)) {
-    if (str_equiv(hook_action, NSSM_HOOK_ACTION_CHANGE)) return true;
-    if (str_equiv(hook_action, NSSM_HOOK_ACTION_RESUME)) return true;
-    if (quiet) return false;
-    print_message(stderr, NSSM_MESSAGE_INVALID_HOOK_ACTION, hook_event);
-    _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_ACTION_CHANGE);
-    _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_ACTION_RESUME);
-    return false;
-  }
-
-  /* Rotate/{Pre,Post} */
-  if (str_equiv(hook_event, NSSM_HOOK_EVENT_ROTATE)) {
-    if (str_equiv(hook_action, NSSM_HOOK_ACTION_PRE)) return true;
-    if (str_equiv(hook_action, NSSM_HOOK_ACTION_POST)) return true;
-    if (quiet) return false;
-    print_message(stderr, NSSM_MESSAGE_INVALID_HOOK_ACTION, hook_event);
-    _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_ACTION_PRE);
-    _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_ACTION_POST);
-    return false;
-  }
-
-  /* Start/{Pre,Post} */
-  if (str_equiv(hook_event, NSSM_HOOK_EVENT_START)) {
-    if (str_equiv(hook_action, NSSM_HOOK_ACTION_PRE)) return true;
-    if (str_equiv(hook_action, NSSM_HOOK_ACTION_POST)) return true;
-    if (quiet) return false;
-    print_message(stderr, NSSM_MESSAGE_INVALID_HOOK_ACTION, hook_event);
-    _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_ACTION_PRE);
-    _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_ACTION_POST);
-    return false;
-  }
-
-  /* Stop/Pre */
-  if (str_equiv(hook_event, NSSM_HOOK_EVENT_STOP)) {
-    if (str_equiv(hook_action, NSSM_HOOK_ACTION_PRE)) return true;
-    if (quiet) return false;
-    print_message(stderr, NSSM_MESSAGE_INVALID_HOOK_ACTION, hook_event);
-    _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_ACTION_PRE);
-    return false;
-  }
-
-  if (quiet) return false;
-  print_message(stderr, NSSM_MESSAGE_INVALID_HOOK_EVENT);
-  _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_EVENT_EXIT);
-  _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_EVENT_POWER);
-  _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_EVENT_ROTATE);
-  _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_EVENT_START);
-  _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_EVENT_STOP);
-  return false;
-}
-
-void await_hook_threads(hook_thread_t *hook_threads, SERVICE_STATUS_HANDLE status_handle, SERVICE_STATUS *status, unsigned long deadline) {
-  if (! hook_threads) return;
-  if (! hook_threads->num_threads) return;
-
-  int *retain = (int *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, hook_threads->num_threads * sizeof(int));
-  if (! retain) {
-    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("retain"), _T("await_hook_threads()"), 0);
-    return;
-  }
-
-  /*
-    We could use WaitForMultipleObjects() but await_single_object() can update
-    the service status as well.
-  */
-  int num_threads = 0;
-  int i;
-  for (i = 0; i < hook_threads->num_threads; i++) {
-    if (deadline) {
-      if (await_single_handle(status_handle, status, hook_threads->data[i].thread_handle, hook_threads->data[i].name, _T(__FUNCTION__), deadline) != 1) {
-        CloseHandle(hook_threads->data[i].thread_handle);
-        continue;
-      }
-    }
-    else if (WaitForSingleObject(hook_threads->data[i].thread_handle, 0) != WAIT_TIMEOUT) {
-      CloseHandle(hook_threads->data[i].thread_handle);
-      continue;
-    }
-
-    retain[num_threads++]= i;
-  }
-
-  if (num_threads) {
-    hook_thread_data_t *data = (hook_thread_data_t *) HeapAlloc(GetProcessHeap(), 0, num_threads * sizeof(hook_thread_data_t));
-    if (! data) {
-    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("data"), _T("await_hook_threads()"), 0);
-      HeapFree(GetProcessHeap(), 0, retain);
-      return;
-    }
-
-    for (i = 0; i < num_threads; i++) memmove(&data[i], &hook_threads->data[retain[i]], sizeof(data[i]));
-
-    HeapFree(GetProcessHeap(), 0, hook_threads->data);
-    hook_threads->data = data;
-    hook_threads->num_threads = num_threads;
-  }
-  else {
-    HeapFree(GetProcessHeap(), 0, hook_threads->data);
-    ZeroMemory(hook_threads, sizeof(*hook_threads));
-  }
-
-  HeapFree(GetProcessHeap(), 0, retain);
-}
-
-/*
-   Returns:
-   NSSM_HOOK_STATUS_SUCCESS  if the hook ran successfully.
-   NSSM_HOOK_STATUS_NOTFOUND if no hook was found.
-   NSSM_HOOK_STATUS_ABORT    if the hook failed and we should cancel service start.
-   NSSM_HOOK_STATUS_ERROR    on error.
-   NSSM_HOOK_STATUS_NOTRUN   if the hook didn't run.
-   NSSM_HOOK_STATUS_TIMEOUT  if the hook timed out.
-   NSSM_HOOK_STATUS_FAILED   if the hook failed.
-*/
-int nssm_hook(hook_thread_t *hook_threads, nssm_service_t *service, TCHAR *hook_event, TCHAR *hook_action, unsigned long *hook_control, unsigned long deadline, bool async) {
-  int ret = 0;
-
-  hook_t *hook = (hook_t *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(hook_t));
-  if (! hook) {
-    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("hook"), _T("nssm_hook()"), 0);
-    return NSSM_HOOK_STATUS_ERROR;
-  }
-
-  FILETIME now;
-  GetSystemTimeAsFileTime(&now);
-
-  EnterCriticalSection(&service->hook_section);
-
-  /* Set the environment. */
-  set_service_environment(service);
-
-  /* ABI version. */
-  TCHAR number[16];
-  _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%lu"), NSSM_HOOK_VERSION);
-  SetEnvironmentVariable(NSSM_HOOK_ENV_VERSION, number);
-
-  /* Event triggering this action. */
-  SetEnvironmentVariable(NSSM_HOOK_ENV_EVENT, hook_event);
-
-  /* Hook action. */
-  SetEnvironmentVariable(NSSM_HOOK_ENV_ACTION, hook_action);
-
-  /* Control triggering this action.  May be empty. */
-  if (hook_control) SetEnvironmentVariable(NSSM_HOOK_ENV_TRIGGER, service_control_text(*hook_control));
-  else SetEnvironmentVariable(NSSM_HOOK_ENV_TRIGGER, _T(""));
-
-  /* Last control handled. */
-  SetEnvironmentVariable(NSSM_HOOK_ENV_LAST_CONTROL, service_control_text(service->last_control));
-
-  /* Path to NSSM, unquoted for the environment. */
-  SetEnvironmentVariable(NSSM_HOOK_ENV_IMAGE_PATH, nssm_unquoted_imagepath());
-
-  /* NSSM version. */
-  SetEnvironmentVariable(NSSM_HOOK_ENV_NSSM_CONFIGURATION, NSSM_CONFIGURATION);
-  SetEnvironmentVariable(NSSM_HOOK_ENV_NSSM_VERSION, NSSM_VERSION);
-  SetEnvironmentVariable(NSSM_HOOK_ENV_BUILD_DATE, NSSM_DATE);
-
-  /* NSSM PID. */
-  _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%lu"), GetCurrentProcessId());
-  SetEnvironmentVariable(NSSM_HOOK_ENV_PID, number);
-
-  /* NSSM runtime. */
-  set_hook_runtime(NSSM_HOOK_ENV_RUNTIME, &service->nssm_creation_time, &now);
-
-  /* Application PID. */
-  if (service->pid) {
-    _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%lu"), service->pid);
-    SetEnvironmentVariable(NSSM_HOOK_ENV_APPLICATION_PID, number);
-    /* Application runtime. */
-    set_hook_runtime(NSSM_HOOK_ENV_APPLICATION_RUNTIME, &service->creation_time, &now);
-    /* Exit code. */
-    SetEnvironmentVariable(NSSM_HOOK_ENV_EXITCODE, _T(""));
-  }
-  else {
-    SetEnvironmentVariable(NSSM_HOOK_ENV_APPLICATION_PID, _T(""));
-    if (str_equiv(hook_event, NSSM_HOOK_EVENT_START) && str_equiv(hook_action, NSSM_HOOK_ACTION_PRE)) {
-      SetEnvironmentVariable(NSSM_HOOK_ENV_APPLICATION_RUNTIME, _T(""));
-      SetEnvironmentVariable(NSSM_HOOK_ENV_EXITCODE, _T(""));
-    }
-    else {
-      set_hook_runtime(NSSM_HOOK_ENV_APPLICATION_RUNTIME, &service->creation_time, &service->exit_time);
-      /* Exit code. */
-      _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%lu"), service->exitcode);
-      SetEnvironmentVariable(NSSM_HOOK_ENV_EXITCODE, number);
-    }
-  }
-
-  /* Deadline for this script. */
-  _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%lu"), deadline);
-  SetEnvironmentVariable(NSSM_HOOK_ENV_DEADLINE, number);
-
-  /* Service name. */
-  SetEnvironmentVariable(NSSM_HOOK_ENV_SERVICE_NAME, service->name);
-  SetEnvironmentVariable(NSSM_HOOK_ENV_SERVICE_DISPLAYNAME, service->displayname);
-
-  /* Times the service was asked to start. */
-  _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%lu"), service->start_requested_count);
-  SetEnvironmentVariable(NSSM_HOOK_ENV_START_REQUESTED_COUNT, number);
-
-  /* Times the service actually did start. */
-  _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%lu"), service->start_count);
-  SetEnvironmentVariable(NSSM_HOOK_ENV_START_COUNT, number);
-
-  /* Times the service exited. */
-  _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%lu"), service->exit_count);
-  SetEnvironmentVariable(NSSM_HOOK_ENV_EXIT_COUNT, number);
-
-  /* Throttled count. */
-  _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%lu"), service->throttle);
-  SetEnvironmentVariable(NSSM_HOOK_ENV_THROTTLE_COUNT, number);
-
-  /* Command line. */
-  TCHAR app[CMD_LENGTH];
-  _sntprintf_s(app, _countof(app), _TRUNCATE, _T("\"%s\" %s"), service->exe, service->flags);
-  SetEnvironmentVariable(NSSM_HOOK_ENV_COMMAND_LINE, app);
-
-  TCHAR cmd[CMD_LENGTH];
-  if (get_hook(service->name, hook_event, hook_action, cmd, sizeof(cmd))) {
-    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_HOOK_FAILED, hook_event, hook_action, service->name, 0);
-    unset_service_environment(service);
-    LeaveCriticalSection(&service->hook_section);
-    HeapFree(GetProcessHeap(), 0, hook);
-    return NSSM_HOOK_STATUS_ERROR;
-  }
-
-  /* No hook. */
-  if (! _tcslen(cmd)) {
-    unset_service_environment(service);
-    LeaveCriticalSection(&service->hook_section);
-    HeapFree(GetProcessHeap(), 0, hook);
-    return NSSM_HOOK_STATUS_NOTFOUND;
-  }
-
-  /* Run the command. */
-  STARTUPINFO si;
-  ZeroMemory(&si, sizeof(si));
-  si.cb = sizeof(si);
-  PROCESS_INFORMATION pi;
-  ZeroMemory(&pi, sizeof(pi));
-  unsigned long flags = 0;
-#ifdef UNICODE
-  flags |= CREATE_UNICODE_ENVIRONMENT;
-#endif
-  ret = NSSM_HOOK_STATUS_NOTRUN;
-  if (CreateProcess(0, cmd, 0, 0, false, flags, 0, service->dir, &si, &pi)) {
-    hook->name = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, HOOK_NAME_LENGTH * sizeof(TCHAR));
-    if (hook->name) _sntprintf_s(hook->name, HOOK_NAME_LENGTH, _TRUNCATE, _T("%s (%s/%s)"), service->name, hook_event, hook_action);
-    hook->process_handle = pi.hProcess;
-    hook->pid = pi.dwProcessId;
-    hook->deadline = deadline;
-    if (get_process_creation_time(hook->process_handle, &hook->creation_time)) GetSystemTimeAsFileTime(&hook->creation_time);
-
-    unsigned long tid;
-    HANDLE thread_handle = CreateThread(NULL, 0, await_hook, (void *) hook, 0, &tid);
-    if (thread_handle) {
-      if (async) {
-        ret = 0;
-        await_hook_threads(hook_threads, service->status_handle, &service->status, 0);
-        add_thread_handle(hook_threads, thread_handle, hook->name);
-      }
-      else {
-        await_single_handle(service->status_handle, &service->status, thread_handle, hook->name, _T(__FUNCTION__), deadline + NSSM_SERVICE_STATUS_DEADLINE);
-        unsigned long exitcode;
-        GetExitCodeThread(thread_handle, &exitcode);
-        ret = (int) exitcode;
-        CloseHandle(thread_handle);
-      }
-    }
-    else {
-      log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);
-      await_hook(hook);
-      if (hook->name) HeapFree(GetProcessHeap(), 0, hook->name);
-      HeapFree(GetProcessHeap(), 0, hook);
-    }
-  }
-  else {
-    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_HOOK_CREATEPROCESS_FAILED, hook_event, hook_action, service->name, cmd, error_string(GetLastError()), 0);
-    HeapFree(GetProcessHeap(), 0, hook);
-  }
-
-  /* Restore our environment. */
-  unset_service_environment(service);
-
-  LeaveCriticalSection(&service->hook_section);
-
-  return ret;
-}
-
-int nssm_hook(hook_thread_t *hook_threads, nssm_service_t *service, TCHAR *hook_event, TCHAR *hook_action, unsigned long *hook_control, unsigned long deadline) {
-  return nssm_hook(hook_threads, service, hook_event, hook_action, hook_control, deadline, true);
-}
-
-int nssm_hook(hook_thread_t *hook_threads, nssm_service_t *service, TCHAR *hook_event, TCHAR *hook_action, unsigned long *hook_control) {
-  return nssm_hook(hook_threads, service, hook_event, hook_action, hook_control, NSSM_HOOK_DEADLINE);
-}
+#include "nssm.h"\r
+\r
+typedef struct {\r
+  TCHAR *name;\r
+  HANDLE process_handle;\r
+  unsigned long pid;\r
+  unsigned long deadline;\r
+  FILETIME creation_time;\r
+  kill_t k;\r
+} hook_t;\r
+\r
+static unsigned long WINAPI await_hook(void *arg) {\r
+  hook_t *hook = (hook_t *) arg;\r
+  if (! hook) return NSSM_HOOK_STATUS_ERROR;\r
+\r
+  int ret = 0;\r
+  if (WaitForSingleObject(hook->process_handle, hook->deadline) == WAIT_TIMEOUT) ret = NSSM_HOOK_STATUS_TIMEOUT;\r
+\r
+  /* Tidy up hook process tree. */\r
+  if (hook->name) hook->k.name = hook->name;\r
+  else hook->k.name = _T("hook");\r
+  hook->k.process_handle = hook->process_handle;\r
+  hook->k.pid = hook->pid;\r
+  hook->k.stop_method = ~0;\r
+  hook->k.kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;\r
+  hook->k.kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;\r
+  hook->k.kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;\r
+  hook->k.creation_time = hook->creation_time;\r
+  GetSystemTimeAsFileTime(&hook->k.exit_time);\r
+  kill_process_tree(&hook->k, hook->pid);\r
+\r
+  if (ret) {\r
+    CloseHandle(hook->process_handle);\r
+    if (hook->name) HeapFree(GetProcessHeap(), 0, hook->name);\r
+    HeapFree(GetProcessHeap(), 0, hook);\r
+    return ret;\r
+  }\r
+\r
+  unsigned long exitcode;\r
+  GetExitCodeProcess(hook->process_handle, &exitcode);\r
+  CloseHandle(hook->process_handle);\r
+\r
+  if (hook->name) HeapFree(GetProcessHeap(), 0, hook->name);\r
+  HeapFree(GetProcessHeap(), 0, hook);\r
+\r
+  if (exitcode == NSSM_HOOK_STATUS_ABORT) return NSSM_HOOK_STATUS_ABORT;\r
+  if (exitcode) return NSSM_HOOK_STATUS_FAILED;\r
+\r
+  return NSSM_HOOK_STATUS_SUCCESS;\r
+}\r
+\r
+static void set_hook_runtime(TCHAR *v, FILETIME *start, FILETIME *now) {\r
+  if (start && now) {\r
+    ULARGE_INTEGER s;\r
+    s.LowPart = start->dwLowDateTime;\r
+    s.HighPart = start->dwHighDateTime;\r
+    if (s.QuadPart) {\r
+      ULARGE_INTEGER t;\r
+      t.LowPart = now->dwLowDateTime;\r
+      t.HighPart = now->dwHighDateTime;\r
+      if (t.QuadPart && t.QuadPart >= s.QuadPart) {\r
+        t.QuadPart -= s.QuadPart;\r
+        t.QuadPart /= 10000LL;\r
+        TCHAR number[16];\r
+        _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%llu"), t.QuadPart);\r
+        SetEnvironmentVariable(v, number);\r
+        return;\r
+      }\r
+    }\r
+  }\r
+  SetEnvironmentVariable(v, _T(""));\r
+}\r
+\r
+static void add_thread_handle(hook_thread_t *hook_threads, HANDLE thread_handle, TCHAR *name) {\r
+  if (! hook_threads) return;\r
+\r
+  int num_threads = hook_threads->num_threads + 1;\r
+  hook_thread_data_t *data = (hook_thread_data_t *) HeapAlloc(GetProcessHeap(), 0, num_threads * sizeof(hook_thread_data_t));\r
+  if (! data) {\r
+    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("hook_thread_t"), _T("add_thread_handle()"), 0);\r
+    return;\r
+  }\r
+\r
+  int i;\r
+  for (i = 0; i < hook_threads->num_threads; i++) memmove(&data[i], &hook_threads->data[i], sizeof(data[i]));\r
+  memmove(data[i].name, name, sizeof(data[i].name));\r
+  data[i].thread_handle = thread_handle;\r
+\r
+  if (hook_threads->data) HeapFree(GetProcessHeap(), 0, hook_threads->data);\r
+  hook_threads->data = data;\r
+  hook_threads->num_threads = num_threads;\r
+}\r
+\r
+bool valid_hook_name(const TCHAR *hook_event, const TCHAR *hook_action, bool quiet) {\r
+  bool valid_event = false;\r
+  bool valid_action = false;\r
+\r
+  /* Exit/Post */\r
+  if (str_equiv(hook_event, NSSM_HOOK_EVENT_EXIT)) {\r
+    if (str_equiv(hook_action, NSSM_HOOK_ACTION_POST)) return true;\r
+    if (quiet) return false;\r
+    print_message(stderr, NSSM_MESSAGE_INVALID_HOOK_ACTION, hook_event);\r
+    _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_ACTION_POST);\r
+    return false;\r
+  }\r
+\r
+  /* Power/{Change,Resume} */\r
+  if (str_equiv(hook_event, NSSM_HOOK_EVENT_POWER)) {\r
+    if (str_equiv(hook_action, NSSM_HOOK_ACTION_CHANGE)) return true;\r
+    if (str_equiv(hook_action, NSSM_HOOK_ACTION_RESUME)) return true;\r
+    if (quiet) return false;\r
+    print_message(stderr, NSSM_MESSAGE_INVALID_HOOK_ACTION, hook_event);\r
+    _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_ACTION_CHANGE);\r
+    _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_ACTION_RESUME);\r
+    return false;\r
+  }\r
+\r
+  /* Rotate/{Pre,Post} */\r
+  if (str_equiv(hook_event, NSSM_HOOK_EVENT_ROTATE)) {\r
+    if (str_equiv(hook_action, NSSM_HOOK_ACTION_PRE)) return true;\r
+    if (str_equiv(hook_action, NSSM_HOOK_ACTION_POST)) return true;\r
+    if (quiet) return false;\r
+    print_message(stderr, NSSM_MESSAGE_INVALID_HOOK_ACTION, hook_event);\r
+    _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_ACTION_PRE);\r
+    _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_ACTION_POST);\r
+    return false;\r
+  }\r
+\r
+  /* Start/{Pre,Post} */\r
+  if (str_equiv(hook_event, NSSM_HOOK_EVENT_START)) {\r
+    if (str_equiv(hook_action, NSSM_HOOK_ACTION_PRE)) return true;\r
+    if (str_equiv(hook_action, NSSM_HOOK_ACTION_POST)) return true;\r
+    if (quiet) return false;\r
+    print_message(stderr, NSSM_MESSAGE_INVALID_HOOK_ACTION, hook_event);\r
+    _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_ACTION_PRE);\r
+    _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_ACTION_POST);\r
+    return false;\r
+  }\r
+\r
+  /* Stop/Pre */\r
+  if (str_equiv(hook_event, NSSM_HOOK_EVENT_STOP)) {\r
+    if (str_equiv(hook_action, NSSM_HOOK_ACTION_PRE)) return true;\r
+    if (quiet) return false;\r
+    print_message(stderr, NSSM_MESSAGE_INVALID_HOOK_ACTION, hook_event);\r
+    _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_ACTION_PRE);\r
+    return false;\r
+  }\r
+\r
+  if (quiet) return false;\r
+  print_message(stderr, NSSM_MESSAGE_INVALID_HOOK_EVENT);\r
+  _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_EVENT_EXIT);\r
+  _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_EVENT_POWER);\r
+  _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_EVENT_ROTATE);\r
+  _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_EVENT_START);\r
+  _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_EVENT_STOP);\r
+  return false;\r
+}\r
+\r
+void await_hook_threads(hook_thread_t *hook_threads, SERVICE_STATUS_HANDLE status_handle, SERVICE_STATUS *status, unsigned long deadline) {\r
+  if (! hook_threads) return;\r
+  if (! hook_threads->num_threads) return;\r
+\r
+  int *retain = (int *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, hook_threads->num_threads * sizeof(int));\r
+  if (! retain) {\r
+    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("retain"), _T("await_hook_threads()"), 0);\r
+    return;\r
+  }\r
+\r
+  /*\r
+    We could use WaitForMultipleObjects() but await_single_object() can update\r
+    the service status as well.\r
+  */\r
+  int num_threads = 0;\r
+  int i;\r
+  for (i = 0; i < hook_threads->num_threads; i++) {\r
+    if (deadline) {\r
+      if (await_single_handle(status_handle, status, hook_threads->data[i].thread_handle, hook_threads->data[i].name, _T(__FUNCTION__), deadline) != 1) {\r
+        CloseHandle(hook_threads->data[i].thread_handle);\r
+        continue;\r
+      }\r
+    }\r
+    else if (WaitForSingleObject(hook_threads->data[i].thread_handle, 0) != WAIT_TIMEOUT) {\r
+      CloseHandle(hook_threads->data[i].thread_handle);\r
+      continue;\r
+    }\r
+\r
+    retain[num_threads++]= i;\r
+  }\r
+\r
+  if (num_threads) {\r
+    hook_thread_data_t *data = (hook_thread_data_t *) HeapAlloc(GetProcessHeap(), 0, num_threads * sizeof(hook_thread_data_t));\r
+    if (! data) {\r
+    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("data"), _T("await_hook_threads()"), 0);\r
+      HeapFree(GetProcessHeap(), 0, retain);\r
+      return;\r
+    }\r
+\r
+    for (i = 0; i < num_threads; i++) memmove(&data[i], &hook_threads->data[retain[i]], sizeof(data[i]));\r
+\r
+    HeapFree(GetProcessHeap(), 0, hook_threads->data);\r
+    hook_threads->data = data;\r
+    hook_threads->num_threads = num_threads;\r
+  }\r
+  else {\r
+    HeapFree(GetProcessHeap(), 0, hook_threads->data);\r
+    ZeroMemory(hook_threads, sizeof(*hook_threads));\r
+  }\r
+\r
+  HeapFree(GetProcessHeap(), 0, retain);\r
+}\r
+\r
+/*\r
+   Returns:\r
+   NSSM_HOOK_STATUS_SUCCESS  if the hook ran successfully.\r
+   NSSM_HOOK_STATUS_NOTFOUND if no hook was found.\r
+   NSSM_HOOK_STATUS_ABORT    if the hook failed and we should cancel service start.\r
+   NSSM_HOOK_STATUS_ERROR    on error.\r
+   NSSM_HOOK_STATUS_NOTRUN   if the hook didn't run.\r
+   NSSM_HOOK_STATUS_TIMEOUT  if the hook timed out.\r
+   NSSM_HOOK_STATUS_FAILED   if the hook failed.\r
+*/\r
+int nssm_hook(hook_thread_t *hook_threads, nssm_service_t *service, TCHAR *hook_event, TCHAR *hook_action, unsigned long *hook_control, unsigned long deadline, bool async) {\r
+  int ret = 0;\r
+\r
+  hook_t *hook = (hook_t *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(hook_t));\r
+  if (! hook) {\r
+    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("hook"), _T("nssm_hook()"), 0);\r
+    return NSSM_HOOK_STATUS_ERROR;\r
+  }\r
+\r
+  FILETIME now;\r
+  GetSystemTimeAsFileTime(&now);\r
+\r
+  EnterCriticalSection(&service->hook_section);\r
+\r
+  /* Set the environment. */\r
+  set_service_environment(service);\r
+\r
+  /* ABI version. */\r
+  TCHAR number[16];\r
+  _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%lu"), NSSM_HOOK_VERSION);\r
+  SetEnvironmentVariable(NSSM_HOOK_ENV_VERSION, number);\r
+\r
+  /* Event triggering this action. */\r
+  SetEnvironmentVariable(NSSM_HOOK_ENV_EVENT, hook_event);\r
+\r
+  /* Hook action. */\r
+  SetEnvironmentVariable(NSSM_HOOK_ENV_ACTION, hook_action);\r
+\r
+  /* Control triggering this action.  May be empty. */\r
+  if (hook_control) SetEnvironmentVariable(NSSM_HOOK_ENV_TRIGGER, service_control_text(*hook_control));\r
+  else SetEnvironmentVariable(NSSM_HOOK_ENV_TRIGGER, _T(""));\r
+\r
+  /* Last control handled. */\r
+  SetEnvironmentVariable(NSSM_HOOK_ENV_LAST_CONTROL, service_control_text(service->last_control));\r
+\r
+  /* Path to NSSM, unquoted for the environment. */\r
+  SetEnvironmentVariable(NSSM_HOOK_ENV_IMAGE_PATH, nssm_unquoted_imagepath());\r
+\r
+  /* NSSM version. */\r
+  SetEnvironmentVariable(NSSM_HOOK_ENV_NSSM_CONFIGURATION, NSSM_CONFIGURATION);\r
+  SetEnvironmentVariable(NSSM_HOOK_ENV_NSSM_VERSION, NSSM_VERSION);\r
+  SetEnvironmentVariable(NSSM_HOOK_ENV_BUILD_DATE, NSSM_DATE);\r
+\r
+  /* NSSM PID. */\r
+  _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%lu"), GetCurrentProcessId());\r
+  SetEnvironmentVariable(NSSM_HOOK_ENV_PID, number);\r
+\r
+  /* NSSM runtime. */\r
+  set_hook_runtime(NSSM_HOOK_ENV_RUNTIME, &service->nssm_creation_time, &now);\r
+\r
+  /* Application PID. */\r
+  if (service->pid) {\r
+    _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%lu"), service->pid);\r
+    SetEnvironmentVariable(NSSM_HOOK_ENV_APPLICATION_PID, number);\r
+    /* Application runtime. */\r
+    set_hook_runtime(NSSM_HOOK_ENV_APPLICATION_RUNTIME, &service->creation_time, &now);\r
+    /* Exit code. */\r
+    SetEnvironmentVariable(NSSM_HOOK_ENV_EXITCODE, _T(""));\r
+  }\r
+  else {\r
+    SetEnvironmentVariable(NSSM_HOOK_ENV_APPLICATION_PID, _T(""));\r
+    if (str_equiv(hook_event, NSSM_HOOK_EVENT_START) && str_equiv(hook_action, NSSM_HOOK_ACTION_PRE)) {\r
+      SetEnvironmentVariable(NSSM_HOOK_ENV_APPLICATION_RUNTIME, _T(""));\r
+      SetEnvironmentVariable(NSSM_HOOK_ENV_EXITCODE, _T(""));\r
+    }\r
+    else {\r
+      set_hook_runtime(NSSM_HOOK_ENV_APPLICATION_RUNTIME, &service->creation_time, &service->exit_time);\r
+      /* Exit code. */\r
+      _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%lu"), service->exitcode);\r
+      SetEnvironmentVariable(NSSM_HOOK_ENV_EXITCODE, number);\r
+    }\r
+  }\r
+\r
+  /* Deadline for this script. */\r
+  _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%lu"), deadline);\r
+  SetEnvironmentVariable(NSSM_HOOK_ENV_DEADLINE, number);\r
+\r
+  /* Service name. */\r
+  SetEnvironmentVariable(NSSM_HOOK_ENV_SERVICE_NAME, service->name);\r
+  SetEnvironmentVariable(NSSM_HOOK_ENV_SERVICE_DISPLAYNAME, service->displayname);\r
+\r
+  /* Times the service was asked to start. */\r
+  _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%lu"), service->start_requested_count);\r
+  SetEnvironmentVariable(NSSM_HOOK_ENV_START_REQUESTED_COUNT, number);\r
+\r
+  /* Times the service actually did start. */\r
+  _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%lu"), service->start_count);\r
+  SetEnvironmentVariable(NSSM_HOOK_ENV_START_COUNT, number);\r
+\r
+  /* Times the service exited. */\r
+  _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%lu"), service->exit_count);\r
+  SetEnvironmentVariable(NSSM_HOOK_ENV_EXIT_COUNT, number);\r
+\r
+  /* Throttled count. */\r
+  _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%lu"), service->throttle);\r
+  SetEnvironmentVariable(NSSM_HOOK_ENV_THROTTLE_COUNT, number);\r
+\r
+  /* Command line. */\r
+  TCHAR app[CMD_LENGTH];\r
+  _sntprintf_s(app, _countof(app), _TRUNCATE, _T("\"%s\" %s"), service->exe, service->flags);\r
+  SetEnvironmentVariable(NSSM_HOOK_ENV_COMMAND_LINE, app);\r
+\r
+  TCHAR cmd[CMD_LENGTH];\r
+  if (get_hook(service->name, hook_event, hook_action, cmd, sizeof(cmd))) {\r
+    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_HOOK_FAILED, hook_event, hook_action, service->name, 0);\r
+    unset_service_environment(service);\r
+    LeaveCriticalSection(&service->hook_section);\r
+    HeapFree(GetProcessHeap(), 0, hook);\r
+    return NSSM_HOOK_STATUS_ERROR;\r
+  }\r
+\r
+  /* No hook. */\r
+  if (! _tcslen(cmd)) {\r
+    unset_service_environment(service);\r
+    LeaveCriticalSection(&service->hook_section);\r
+    HeapFree(GetProcessHeap(), 0, hook);\r
+    return NSSM_HOOK_STATUS_NOTFOUND;\r
+  }\r
+\r
+  /* Run the command. */\r
+  STARTUPINFO si;\r
+  ZeroMemory(&si, sizeof(si));\r
+  si.cb = sizeof(si);\r
+  PROCESS_INFORMATION pi;\r
+  ZeroMemory(&pi, sizeof(pi));\r
+  unsigned long flags = 0;\r
+#ifdef UNICODE\r
+  flags |= CREATE_UNICODE_ENVIRONMENT;\r
+#endif\r
+  ret = NSSM_HOOK_STATUS_NOTRUN;\r
+  if (CreateProcess(0, cmd, 0, 0, false, flags, 0, service->dir, &si, &pi)) {\r
+    hook->name = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, HOOK_NAME_LENGTH * sizeof(TCHAR));\r
+    if (hook->name) _sntprintf_s(hook->name, HOOK_NAME_LENGTH, _TRUNCATE, _T("%s (%s/%s)"), service->name, hook_event, hook_action);\r
+    hook->process_handle = pi.hProcess;\r
+    hook->pid = pi.dwProcessId;\r
+    hook->deadline = deadline;\r
+    if (get_process_creation_time(hook->process_handle, &hook->creation_time)) GetSystemTimeAsFileTime(&hook->creation_time);\r
+\r
+    unsigned long tid;\r
+    HANDLE thread_handle = CreateThread(NULL, 0, await_hook, (void *) hook, 0, &tid);\r
+    if (thread_handle) {\r
+      if (async) {\r
+        ret = 0;\r
+        await_hook_threads(hook_threads, service->status_handle, &service->status, 0);\r
+        add_thread_handle(hook_threads, thread_handle, hook->name);\r
+      }\r
+      else {\r
+        await_single_handle(service->status_handle, &service->status, thread_handle, hook->name, _T(__FUNCTION__), deadline + NSSM_SERVICE_STATUS_DEADLINE);\r
+        unsigned long exitcode;\r
+        GetExitCodeThread(thread_handle, &exitcode);\r
+        ret = (int) exitcode;\r
+        CloseHandle(thread_handle);\r
+      }\r
+    }\r
+    else {\r
+      log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);\r
+      await_hook(hook);\r
+      if (hook->name) HeapFree(GetProcessHeap(), 0, hook->name);\r
+      HeapFree(GetProcessHeap(), 0, hook);\r
+    }\r
+  }\r
+  else {\r
+    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_HOOK_CREATEPROCESS_FAILED, hook_event, hook_action, service->name, cmd, error_string(GetLastError()), 0);\r
+    HeapFree(GetProcessHeap(), 0, hook);\r
+  }\r
+\r
+  /* Restore our environment. */\r
+  unset_service_environment(service);\r
+\r
+  LeaveCriticalSection(&service->hook_section);\r
+\r
+  return ret;\r
+}\r
+\r
+int nssm_hook(hook_thread_t *hook_threads, nssm_service_t *service, TCHAR *hook_event, TCHAR *hook_action, unsigned long *hook_control, unsigned long deadline) {\r
+  return nssm_hook(hook_threads, service, hook_event, hook_action, hook_control, deadline, true);\r
+}\r
+\r
+int nssm_hook(hook_thread_t *hook_threads, nssm_service_t *service, TCHAR *hook_event, TCHAR *hook_action, unsigned long *hook_control) {\r
+  return nssm_hook(hook_threads, service, hook_event, hook_action, hook_control, NSSM_HOOK_DEADLINE);\r
+}\r
diff --git a/hook.h b/hook.h
index 045e4aa..85e211e 100644 (file)
--- a/hook.h
+++ b/hook.h
@@ -1,75 +1,75 @@
-#ifndef HOOK_H
-#define HOOK_H
-
-#define NSSM_HOOK_EVENT_START _T("Start")
-#define NSSM_HOOK_EVENT_STOP _T("Stop")
-#define NSSM_HOOK_EVENT_EXIT _T("Exit")
-#define NSSM_HOOK_EVENT_POWER _T("Power")
-#define NSSM_HOOK_EVENT_ROTATE _T("Rotate")
-
-#define NSSM_HOOK_ACTION_PRE _T("Pre")
-#define NSSM_HOOK_ACTION_POST _T("Post")
-#define NSSM_HOOK_ACTION_CHANGE _T("Change")
-#define NSSM_HOOK_ACTION_RESUME _T("Resume")
-
-/* Hook name will be "<service> (<event>/<action>)" */
-#define HOOK_NAME_LENGTH SERVICE_NAME_LENGTH * 2
-
-#define NSSM_HOOK_VERSION 1
-
-/* Hook ran successfully. */
-#define NSSM_HOOK_STATUS_SUCCESS 0
-/* No hook configured. */
-#define NSSM_HOOK_STATUS_NOTFOUND 1
-/* Hook requested abort. */
-#define NSSM_HOOK_STATUS_ABORT 99
-/* Internal error launching hook. */
-#define NSSM_HOOK_STATUS_ERROR 100
-/* Hook was not run. */
-#define NSSM_HOOK_STATUS_NOTRUN 101
-/* Hook timed out. */
-#define NSSM_HOOK_STATUS_TIMEOUT 102
-/* Hook returned non-zero. */
-#define NSSM_HOOK_STATUS_FAILED 111
-
-/* Version 1. */
-#define NSSM_HOOK_ENV_VERSION _T("NSSM_HOOK_VERSION")
-#define NSSM_HOOK_ENV_IMAGE_PATH _T("NSSM_EXE")
-#define NSSM_HOOK_ENV_NSSM_CONFIGURATION _T("NSSM_CONFIGURATION")
-#define NSSM_HOOK_ENV_NSSM_VERSION _T("NSSM_VERSION")
-#define NSSM_HOOK_ENV_BUILD_DATE _T("NSSM_BUILD_DATE")
-#define NSSM_HOOK_ENV_PID _T("NSSM_PID")
-#define NSSM_HOOK_ENV_DEADLINE _T("NSSM_DEADLINE")
-#define NSSM_HOOK_ENV_SERVICE_NAME _T("NSSM_SERVICE_NAME")
-#define NSSM_HOOK_ENV_SERVICE_DISPLAYNAME _T("NSSM_SERVICE_DISPLAYNAME")
-#define NSSM_HOOK_ENV_COMMAND_LINE _T("NSSM_COMMAND_LINE")
-#define NSSM_HOOK_ENV_APPLICATION_PID _T("NSSM_APPLICATION_PID")
-#define NSSM_HOOK_ENV_EVENT _T("NSSM_EVENT")
-#define NSSM_HOOK_ENV_ACTION _T("NSSM_ACTION")
-#define NSSM_HOOK_ENV_TRIGGER _T("NSSM_TRIGGER")
-#define NSSM_HOOK_ENV_LAST_CONTROL _T("NSSM_LAST_CONTROL")
-#define NSSM_HOOK_ENV_START_REQUESTED_COUNT _T("NSSM_START_REQUESTED_COUNT")
-#define NSSM_HOOK_ENV_START_COUNT _T("NSSM_START_COUNT")
-#define NSSM_HOOK_ENV_THROTTLE_COUNT _T("NSSM_THROTTLE_COUNT")
-#define NSSM_HOOK_ENV_EXIT_COUNT _T("NSSM_EXIT_COUNT")
-#define NSSM_HOOK_ENV_EXITCODE _T("NSSM_EXITCODE")
-#define NSSM_HOOK_ENV_RUNTIME _T("NSSM_RUNTIME")
-#define NSSM_HOOK_ENV_APPLICATION_RUNTIME _T("NSSM_APPLICATION_RUNTIME")
-
-typedef struct {
-  TCHAR name[HOOK_NAME_LENGTH];
-  HANDLE thread_handle;
-} hook_thread_data_t;
-
-typedef struct {
-  hook_thread_data_t *data;
-  int num_threads;
-} hook_thread_t;
-
-bool valid_hook_name(const TCHAR *, const TCHAR *, bool);
-void await_hook_threads(hook_thread_t *, SERVICE_STATUS_HANDLE, SERVICE_STATUS *, unsigned long);
-int nssm_hook(hook_thread_t *, nssm_service_t *, TCHAR *, TCHAR *, unsigned long *, unsigned long, bool);
-int nssm_hook(hook_thread_t *, nssm_service_t *, TCHAR *, TCHAR *, unsigned long *, unsigned long);
-int nssm_hook(hook_thread_t *, nssm_service_t *, TCHAR *, TCHAR *, unsigned long *);
-
-#endif
+#ifndef HOOK_H\r
+#define HOOK_H\r
+\r
+#define NSSM_HOOK_EVENT_START _T("Start")\r
+#define NSSM_HOOK_EVENT_STOP _T("Stop")\r
+#define NSSM_HOOK_EVENT_EXIT _T("Exit")\r
+#define NSSM_HOOK_EVENT_POWER _T("Power")\r
+#define NSSM_HOOK_EVENT_ROTATE _T("Rotate")\r
+\r
+#define NSSM_HOOK_ACTION_PRE _T("Pre")\r
+#define NSSM_HOOK_ACTION_POST _T("Post")\r
+#define NSSM_HOOK_ACTION_CHANGE _T("Change")\r
+#define NSSM_HOOK_ACTION_RESUME _T("Resume")\r
+\r
+/* Hook name will be "<service> (<event>/<action>)" */\r
+#define HOOK_NAME_LENGTH SERVICE_NAME_LENGTH * 2\r
+\r
+#define NSSM_HOOK_VERSION 1\r
+\r
+/* Hook ran successfully. */\r
+#define NSSM_HOOK_STATUS_SUCCESS 0\r
+/* No hook configured. */\r
+#define NSSM_HOOK_STATUS_NOTFOUND 1\r
+/* Hook requested abort. */\r
+#define NSSM_HOOK_STATUS_ABORT 99\r
+/* Internal error launching hook. */\r
+#define NSSM_HOOK_STATUS_ERROR 100\r
+/* Hook was not run. */\r
+#define NSSM_HOOK_STATUS_NOTRUN 101\r
+/* Hook timed out. */\r
+#define NSSM_HOOK_STATUS_TIMEOUT 102\r
+/* Hook returned non-zero. */\r
+#define NSSM_HOOK_STATUS_FAILED 111\r
+\r
+/* Version 1. */\r
+#define NSSM_HOOK_ENV_VERSION _T("NSSM_HOOK_VERSION")\r
+#define NSSM_HOOK_ENV_IMAGE_PATH _T("NSSM_EXE")\r
+#define NSSM_HOOK_ENV_NSSM_CONFIGURATION _T("NSSM_CONFIGURATION")\r
+#define NSSM_HOOK_ENV_NSSM_VERSION _T("NSSM_VERSION")\r
+#define NSSM_HOOK_ENV_BUILD_DATE _T("NSSM_BUILD_DATE")\r
+#define NSSM_HOOK_ENV_PID _T("NSSM_PID")\r
+#define NSSM_HOOK_ENV_DEADLINE _T("NSSM_DEADLINE")\r
+#define NSSM_HOOK_ENV_SERVICE_NAME _T("NSSM_SERVICE_NAME")\r
+#define NSSM_HOOK_ENV_SERVICE_DISPLAYNAME _T("NSSM_SERVICE_DISPLAYNAME")\r
+#define NSSM_HOOK_ENV_COMMAND_LINE _T("NSSM_COMMAND_LINE")\r
+#define NSSM_HOOK_ENV_APPLICATION_PID _T("NSSM_APPLICATION_PID")\r
+#define NSSM_HOOK_ENV_EVENT _T("NSSM_EVENT")\r
+#define NSSM_HOOK_ENV_ACTION _T("NSSM_ACTION")\r
+#define NSSM_HOOK_ENV_TRIGGER _T("NSSM_TRIGGER")\r
+#define NSSM_HOOK_ENV_LAST_CONTROL _T("NSSM_LAST_CONTROL")\r
+#define NSSM_HOOK_ENV_START_REQUESTED_COUNT _T("NSSM_START_REQUESTED_COUNT")\r
+#define NSSM_HOOK_ENV_START_COUNT _T("NSSM_START_COUNT")\r
+#define NSSM_HOOK_ENV_THROTTLE_COUNT _T("NSSM_THROTTLE_COUNT")\r
+#define NSSM_HOOK_ENV_EXIT_COUNT _T("NSSM_EXIT_COUNT")\r
+#define NSSM_HOOK_ENV_EXITCODE _T("NSSM_EXITCODE")\r
+#define NSSM_HOOK_ENV_RUNTIME _T("NSSM_RUNTIME")\r
+#define NSSM_HOOK_ENV_APPLICATION_RUNTIME _T("NSSM_APPLICATION_RUNTIME")\r
+\r
+typedef struct {\r
+  TCHAR name[HOOK_NAME_LENGTH];\r
+  HANDLE thread_handle;\r
+} hook_thread_data_t;\r
+\r
+typedef struct {\r
+  hook_thread_data_t *data;\r
+  int num_threads;\r
+} hook_thread_t;\r
+\r
+bool valid_hook_name(const TCHAR *, const TCHAR *, bool);\r
+void await_hook_threads(hook_thread_t *, SERVICE_STATUS_HANDLE, SERVICE_STATUS *, unsigned long);\r
+int nssm_hook(hook_thread_t *, nssm_service_t *, TCHAR *, TCHAR *, unsigned long *, unsigned long, bool);\r
+int nssm_hook(hook_thread_t *, nssm_service_t *, TCHAR *, TCHAR *, unsigned long *, unsigned long);\r
+int nssm_hook(hook_thread_t *, nssm_service_t *, TCHAR *, TCHAR *, unsigned long *);\r
+\r
+#endif\r
index 6257131..ec3a2a8 100644 (file)
@@ -1,92 +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));
-}
+#include "nssm.h"\r
+\r
+imports_t imports;\r
+\r
+/*\r
+  Try to set up function pointers.\r
+  In this first implementation it is not an error if we can't load them\r
+  because we aren't currently trying to load any functions which we\r
+  absolutely need.  If we later add some indispensible imports we can\r
+  return non-zero here to force an application exit.\r
+*/\r
+HMODULE get_dll(const TCHAR *dll, unsigned long *error) {\r
+  *error = 0;\r
+\r
+  HMODULE ret = LoadLibrary(dll);\r
+  if (! ret) {\r
+    *error = GetLastError();\r
+    log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_LOADLIBRARY_FAILED, dll, error_string(*error), 0);\r
+  }\r
+\r
+  return ret;\r
+}\r
+\r
+FARPROC get_import(HMODULE library, const char *function, unsigned long *error) {\r
+  *error = 0;\r
+\r
+  FARPROC ret = GetProcAddress(library, function);\r
+  if (! ret) {\r
+    *error = GetLastError();\r
+    TCHAR *function_name;\r
+#ifdef UNICODE\r
+    size_t buflen;\r
+    mbstowcs_s(&buflen, NULL, 0, function, _TRUNCATE);\r
+    function_name = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, buflen * sizeof(TCHAR));\r
+    if (function_name) mbstowcs_s(&buflen, function_name, buflen * sizeof(TCHAR), function, _TRUNCATE);\r
+#else\r
+    function_name = (TCHAR *) function;\r
+#endif\r
+    if (*error != ERROR_PROC_NOT_FOUND) log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_GETPROCADDRESS_FAILED, function_name, error_string(*error), 0);\r
+#ifdef UNICODE\r
+    if (function_name) HeapFree(GetProcessHeap(), 0, function_name);\r
+#endif\r
+  }\r
+\r
+  return ret;\r
+}\r
+\r
+int get_imports() {\r
+  unsigned long error;\r
+\r
+  ZeroMemory(&imports, sizeof(imports));\r
+\r
+  imports.kernel32 = get_dll(_T("kernel32.dll"), &error);\r
+  if (imports.kernel32) {\r
+    imports.AttachConsole = (AttachConsole_ptr) get_import(imports.kernel32, "AttachConsole", &error);\r
+    if (! imports.AttachConsole) {\r
+      if (error != ERROR_PROC_NOT_FOUND) return 2;\r
+    }\r
+\r
+    imports.SleepConditionVariableCS = (SleepConditionVariableCS_ptr) get_import(imports.kernel32, "SleepConditionVariableCS", &error);\r
+    if (! imports.SleepConditionVariableCS) {\r
+      if (error != ERROR_PROC_NOT_FOUND) return 3;\r
+    }\r
+\r
+    imports.WakeConditionVariable = (WakeConditionVariable_ptr) get_import(imports.kernel32, "WakeConditionVariable", &error);\r
+    if (! imports.WakeConditionVariable) {\r
+      if (error != ERROR_PROC_NOT_FOUND) return 4;\r
+    }\r
+  }\r
+  else if (error != ERROR_MOD_NOT_FOUND) return 1;\r
+\r
+  imports.advapi32 = get_dll(_T("advapi32.dll"), &error);\r
+  if (imports.advapi32) {\r
+    imports.CreateWellKnownSid = (CreateWellKnownSid_ptr) get_import(imports.advapi32, "CreateWellKnownSid", &error);\r
+    if (! imports.CreateWellKnownSid) {\r
+      if (error != ERROR_PROC_NOT_FOUND) return 6;\r
+    }\r
+    imports.IsWellKnownSid = (IsWellKnownSid_ptr) get_import(imports.advapi32, "IsWellKnownSid", &error);\r
+    if (! imports.IsWellKnownSid) {\r
+      if (error != ERROR_PROC_NOT_FOUND) return 7;\r
+    }\r
+  }\r
+  else if (error != ERROR_MOD_NOT_FOUND) return 5;\r
+\r
+  return 0;\r
+}\r
+\r
+void free_imports() {\r
+  if (imports.kernel32) FreeLibrary(imports.kernel32);\r
+  if (imports.advapi32) FreeLibrary(imports.advapi32);\r
+  ZeroMemory(&imports, sizeof(imports));\r
+}\r
index 03e2df1..008a702 100644 (file)
--- a/imports.h
+++ b/imports.h
@@ -1,25 +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
+#ifndef IMPORTS_H\r
+#define IMPORTS_H\r
+\r
+typedef BOOL (WINAPI *AttachConsole_ptr)(DWORD);\r
+typedef BOOL (WINAPI *SleepConditionVariableCS_ptr)(PCONDITION_VARIABLE, PCRITICAL_SECTION, DWORD);\r
+typedef void (WINAPI *WakeConditionVariable_ptr)(PCONDITION_VARIABLE);\r
+typedef BOOL (WINAPI *CreateWellKnownSid_ptr)(WELL_KNOWN_SID_TYPE, SID *, SID *, unsigned long *);\r
+typedef BOOL (WINAPI *IsWellKnownSid_ptr)(SID *, WELL_KNOWN_SID_TYPE);\r
+\r
+typedef struct {\r
+  HMODULE kernel32;\r
+  HMODULE advapi32;\r
+  AttachConsole_ptr AttachConsole;\r
+  SleepConditionVariableCS_ptr SleepConditionVariableCS;\r
+  WakeConditionVariable_ptr WakeConditionVariable;\r
+  CreateWellKnownSid_ptr CreateWellKnownSid;\r
+  IsWellKnownSid_ptr IsWellKnownSid;\r
+} imports_t;\r
+\r
+HMODULE get_dll(const TCHAR *, unsigned long *);\r
+FARPROC get_import(HMODULE, const char *, unsigned long *);\r
+int get_imports();\r
+void free_imports();\r
+\r
+#endif\r
index 985a4b4..14ce8df 100644 (file)
Binary files a/messages.mc and b/messages.mc differ
index b03b504..d1a9a49 100644 (file)
-#include "nssm.h"
-
-extern imports_t imports;
-
-void service_kill_t(nssm_service_t *service, kill_t *k) {
-  if (! service) return;
-  if (! k) return;
-
-  ZeroMemory(k, sizeof(*k));
-  k->name = service->name;
-  k->process_handle = service->process_handle;
-  k->pid = service->pid;
-  k->exitcode = service->exitcode;
-  k->stop_method = service->stop_method;
-  k->kill_console_delay = service->kill_console_delay;
-  k->kill_window_delay = service->kill_window_delay;
-  k->kill_threads_delay = service->kill_threads_delay;
-  k->status_handle = service->status_handle;
-  k->status = &service->status;
-  k->creation_time = service->creation_time;
-  k->exit_time = service->exit_time;
-}
-
-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(kill_t *k, 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, k->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(&k->creation_time, &ft) > 0) return 4;
-
-  /* Verify that the parent's exit time is not earlier. */
-  if (CompareFileTime(&k->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(nssm_service_t *service, 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, k->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, k->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, k->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;
-}
-
-int kill_threads(kill_t *k) {
-  return kill_threads(NULL, k);
-}
-
-/* Give the process a chance to die gracefully. */
-int kill_process(nssm_service_t *service, kill_t *k) {
-  if (! k) return 1;
-
-  unsigned long ret;
-  if (GetExitCodeProcess(k->process_handle, &ret)) {
-    if (ret != STILL_ACTIVE) return 1;
-  }
-
-  /* Try to send a Control-C event to the console. */
-  if (k->stop_method & NSSM_STOP_METHOD_CONSOLE) {
-    if (! kill_console(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 (k->stop_method & NSSM_STOP_METHOD_WINDOW) {
-    EnumWindows((WNDENUMPROC) kill_window, (LPARAM) k);
-    if (k->signalled) {
-      if (! await_single_handle(k->status_handle, k->status, k->process_handle, k->name, _T(__FUNCTION__), k->kill_window_delay)) return 1;
-      k->signalled = 0;
-    }
-  }
-
-  /*
-    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 (k->stop_method & NSSM_STOP_METHOD_THREADS) {
-    if (kill_threads(k)) {
-      if (! await_single_handle(k->status_handle, k->status, k->process_handle, k->name, _T(__FUNCTION__), k->kill_threads_delay)) return 1;
-    }
-  }
-
-  /* We tried being nice.  Time for extreme prejudice. */
-  if (k->stop_method & NSSM_STOP_METHOD_TERMINATE) {
-    return TerminateProcess(k->process_handle, k->exitcode);
-  }
-
-  return 0;
-}
-
-int kill_process(kill_t *k) {
-  return kill_process(NULL, k);
-}
-
-/* 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 (! k) 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, k->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, k->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, k->name, error_string(GetLastError()), 0);
-      ret = 5;
-    }
-  }
-
-  /* Detach from the console. */
-  if (! FreeConsole()) {
-    log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_FREECONSOLE_FAILED, k->name, error_string(GetLastError()), 0);
-  }
-
-  /* Wait for process to exit. */
-  if (await_single_handle(k->status_handle, k->status, k->process_handle, k->name, _T(__FUNCTION__), k->kill_console_delay)) ret = 6;
-
-  return ret;
-}
-
-int kill_console(kill_t *k) {
-  return kill_console(NULL, k);
-}
-
-void kill_process_tree(nssm_service_t * service, kill_t *k, unsigned long ppid) {
-  if (! k) return;
-  /* Shouldn't happen unless the service failed to start. */
-  if (! k->pid) return; /* XXX: needed? */
-  unsigned long pid = k->pid;
-
-  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"), k->exitcode);
-  log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_KILLING, k->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, k->name, 0);
-    k->process_handle = process_handle; /* XXX: open directly? */
-    if (! kill_process(k)) {
-      /* Maybe it already died. */
-      unsigned long ret;
-      if (! GetExitCodeProcess(process_handle, &ret) || ret == STILL_ACTIVE) {
-        if (k->stop_method & NSSM_STOP_METHOD_TERMINATE) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_TERMINATEPROCESS_FAILED, pid_string, k->name, error_string(GetLastError()), 0);
-        else log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_PROCESS_STILL_ACTIVE, k->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, k->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, k->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, k->name, error_string(GetLastError()), 0);
-    CloseHandle(snapshot);
-    return;
-  }
-
-  /* This is a child of the doomed process so kill it. */
-  if (! check_parent(k, &pe, pid)) {
-    k->pid = pe.th32ProcessID;
-    kill_process_tree(k, ppid);
-  }
-  k->pid = pid;
-
-  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, k->name, error_string(GetLastError()), 0);
-      CloseHandle(snapshot);
-      return;
-    }
-
-    if (! check_parent(k, &pe, pid)) {
-      k->pid = pe.th32ProcessID;
-      kill_process_tree(k, ppid);
-    }
-    k->pid = pid;
-  }
-
-  CloseHandle(snapshot);
-}
-
-void kill_process_tree(kill_t *k, unsigned long ppid) {
-  return kill_process_tree(NULL, k, ppid);
-}
+#include "nssm.h"\r
+\r
+extern imports_t imports;\r
+\r
+void service_kill_t(nssm_service_t *service, kill_t *k) {\r
+  if (! service) return;\r
+  if (! k) return;\r
+\r
+  ZeroMemory(k, sizeof(*k));\r
+  k->name = service->name;\r
+  k->process_handle = service->process_handle;\r
+  k->pid = service->pid;\r
+  k->exitcode = service->exitcode;\r
+  k->stop_method = service->stop_method;\r
+  k->kill_console_delay = service->kill_console_delay;\r
+  k->kill_window_delay = service->kill_window_delay;\r
+  k->kill_threads_delay = service->kill_threads_delay;\r
+  k->status_handle = service->status_handle;\r
+  k->status = &service->status;\r
+  k->creation_time = service->creation_time;\r
+  k->exit_time = service->exit_time;\r
+}\r
+\r
+int get_process_creation_time(HANDLE process_handle, FILETIME *ft) {\r
+  FILETIME creation_time, exit_time, kernel_time, user_time;\r
+\r
+  if (! GetProcessTimes(process_handle, &creation_time, &exit_time, &kernel_time, &user_time)) {\r
+    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GETPROCESSTIMES_FAILED, error_string(GetLastError()), 0);\r
+    return 1;\r
+  }\r
+\r
+  memmove(ft, &creation_time, sizeof(creation_time));\r
+\r
+  return 0;\r
+}\r
+\r
+int get_process_exit_time(HANDLE process_handle, FILETIME *ft) {\r
+  FILETIME creation_time, exit_time, kernel_time, user_time;\r
+\r
+  if (! GetProcessTimes(process_handle, &creation_time, &exit_time, &kernel_time, &user_time)) {\r
+    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GETPROCESSTIMES_FAILED, error_string(GetLastError()), 0);\r
+    return 1;\r
+  }\r
+\r
+  if (! (exit_time.dwLowDateTime || exit_time.dwHighDateTime)) return 2;\r
+  memmove(ft, &exit_time, sizeof(exit_time));\r
+\r
+  return 0;\r
+}\r
+\r
+int check_parent(kill_t *k, PROCESSENTRY32 *pe, unsigned long ppid) {\r
+  /* Check parent process ID matches. */\r
+  if (pe->th32ParentProcessID != ppid) return 1;\r
+\r
+  /*\r
+    Process IDs can be reused so do a sanity check by making sure the child\r
+    has been running for less time than the parent.\r
+    Though unlikely, it's possible that the parent exited and its process ID\r
+    was already reused, so we'll also compare against its exit time.\r
+  */\r
+  HANDLE process_handle = OpenProcess(PROCESS_QUERY_INFORMATION, false, pe->th32ProcessID);\r
+  if (! process_handle) {\r
+    TCHAR pid_string[16];\r
+    _sntprintf_s(pid_string, _countof(pid_string), _TRUNCATE, _T("%lu"), pe->th32ProcessID);\r
+    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENPROCESS_FAILED, pid_string, k->name, error_string(GetLastError()), 0);\r
+    return 2;\r
+  }\r
+\r
+  FILETIME ft;\r
+  if (get_process_creation_time(process_handle, &ft)) {\r
+    CloseHandle(process_handle);\r
+    return 3;\r
+  }\r
+\r
+  CloseHandle(process_handle);\r
+\r
+  /* Verify that the parent's creation time is not later. */\r
+  if (CompareFileTime(&k->creation_time, &ft) > 0) return 4;\r
+\r
+  /* Verify that the parent's exit time is not earlier. */\r
+  if (CompareFileTime(&k->exit_time, &ft) < 0) return 5;\r
+\r
+  return 0;\r
+}\r
+\r
+/* Send some window messages and hope the window respects one or more. */\r
+int CALLBACK kill_window(HWND window, LPARAM arg) {\r
+  kill_t *k = (kill_t *) arg;\r
+\r
+  unsigned long pid;\r
+  if (! GetWindowThreadProcessId(window, &pid)) return 1;\r
+  if (pid != k->pid) return 1;\r
+\r
+  /* First try sending WM_CLOSE to request that the window close. */\r
+  k->signalled |= PostMessage(window, WM_CLOSE, k->exitcode, 0);\r
+\r
+  /*\r
+    Then tell the window that the user is logging off and it should exit\r
+    without worrying about saving any data.\r
+  */\r
+  k->signalled |= PostMessage(window, WM_ENDSESSION, 1, ENDSESSION_CLOSEAPP | ENDSESSION_CRITICAL | ENDSESSION_LOGOFF);\r
+\r
+  return 1;\r
+}\r
+\r
+/*\r
+  Try to post a message to the message queues of threads associated with the\r
+  given process ID.  Not all threads have message queues so there's no\r
+  guarantee of success, and we don't want to be left waiting for unsignalled\r
+  processes so this function returns only true if at least one thread was\r
+  successfully prodded.\r
+*/\r
+int kill_threads(nssm_service_t *service, kill_t *k) {\r
+  int ret = 0;\r
+\r
+  /* Get a snapshot of all threads in the system. */\r
+  HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);\r
+  if (! snapshot) {\r
+    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETOOLHELP32SNAPSHOT_THREAD_FAILED, k->name, error_string(GetLastError()), 0);\r
+    return 0;\r
+  }\r
+\r
+  THREADENTRY32 te;\r
+  ZeroMemory(&te, sizeof(te));\r
+  te.dwSize = sizeof(te);\r
+\r
+  if (! Thread32First(snapshot, &te)) {\r
+    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_THREAD_ENUMERATE_FAILED, k->name, error_string(GetLastError()), 0);\r
+    CloseHandle(snapshot);\r
+    return 0;\r
+  }\r
+\r
+  /* This thread belongs to the doomed process so signal it. */\r
+  if (te.th32OwnerProcessID == k->pid) {\r
+    ret |= PostThreadMessage(te.th32ThreadID, WM_QUIT, k->exitcode, 0);\r
+  }\r
+\r
+  while (true) {\r
+    /* Try to get the next thread. */\r
+    if (! Thread32Next(snapshot, &te)) {\r
+      unsigned long error = GetLastError();\r
+      if (error == ERROR_NO_MORE_FILES) break;\r
+      log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_THREAD_ENUMERATE_FAILED, k->name, error_string(GetLastError()), 0);\r
+      CloseHandle(snapshot);\r
+      return ret;\r
+    }\r
+\r
+    if (te.th32OwnerProcessID == k->pid) {\r
+      ret |= PostThreadMessage(te.th32ThreadID, WM_QUIT, k->exitcode, 0);\r
+    }\r
+  }\r
+\r
+  CloseHandle(snapshot);\r
+\r
+  return ret;\r
+}\r
+\r
+int kill_threads(kill_t *k) {\r
+  return kill_threads(NULL, k);\r
+}\r
+\r
+/* Give the process a chance to die gracefully. */\r
+int kill_process(nssm_service_t *service, kill_t *k) {\r
+  if (! k) return 1;\r
+\r
+  unsigned long ret;\r
+  if (GetExitCodeProcess(k->process_handle, &ret)) {\r
+    if (ret != STILL_ACTIVE) return 1;\r
+  }\r
+\r
+  /* Try to send a Control-C event to the console. */\r
+  if (k->stop_method & NSSM_STOP_METHOD_CONSOLE) {\r
+    if (! kill_console(k)) return 1;\r
+  }\r
+\r
+  /*\r
+    Try to post messages to the windows belonging to the given process ID.\r
+    If the process is a console application it won't have any windows so there's\r
+    no guarantee of success.\r
+  */\r
+  if (k->stop_method & NSSM_STOP_METHOD_WINDOW) {\r
+    EnumWindows((WNDENUMPROC) kill_window, (LPARAM) k);\r
+    if (k->signalled) {\r
+      if (! await_single_handle(k->status_handle, k->status, k->process_handle, k->name, _T(__FUNCTION__), k->kill_window_delay)) return 1;\r
+      k->signalled = 0;\r
+    }\r
+  }\r
+\r
+  /*\r
+    Try to post messages to any thread message queues associated with the\r
+    process.  Console applications might have them (but probably won't) so\r
+    there's still no guarantee of success.\r
+  */\r
+  if (k->stop_method & NSSM_STOP_METHOD_THREADS) {\r
+    if (kill_threads(k)) {\r
+      if (! await_single_handle(k->status_handle, k->status, k->process_handle, k->name, _T(__FUNCTION__), k->kill_threads_delay)) return 1;\r
+    }\r
+  }\r
+\r
+  /* We tried being nice.  Time for extreme prejudice. */\r
+  if (k->stop_method & NSSM_STOP_METHOD_TERMINATE) {\r
+    return TerminateProcess(k->process_handle, k->exitcode);\r
+  }\r
+\r
+  return 0;\r
+}\r
+\r
+int kill_process(kill_t *k) {\r
+  return kill_process(NULL, k);\r
+}\r
+\r
+/* Simulate a Control-C event to our console (shared with the app). */\r
+int kill_console(nssm_service_t *service, kill_t *k) {\r
+  unsigned long ret;\r
+\r
+  if (! k) return 1;\r
+\r
+  /* Check we loaded AttachConsole(). */\r
+  if (! imports.AttachConsole) return 4;\r
+\r
+  /* Try to attach to the process's console. */\r
+  if (! imports.AttachConsole(k->pid)) {\r
+    ret = GetLastError();\r
+\r
+    switch (ret) {\r
+      case ERROR_INVALID_HANDLE:\r
+        /* The app doesn't have a console. */\r
+        return 1;\r
+\r
+      case ERROR_GEN_FAILURE:\r
+        /* The app already exited. */\r
+        return 2;\r
+\r
+      case ERROR_ACCESS_DENIED:\r
+      default:\r
+        /* We already have a console. */\r
+        log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_ATTACHCONSOLE_FAILED, k->name, error_string(ret), 0);\r
+        return 3;\r
+    }\r
+  }\r
+\r
+  /* Ignore the event ourselves. */\r
+  ret = 0;\r
+  if (! SetConsoleCtrlHandler(0, TRUE)) {\r
+    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETCONSOLECTRLHANDLER_FAILED, k->name, error_string(GetLastError()), 0);\r
+    ret = 4;\r
+  }\r
+\r
+  /* Send the event. */\r
+  if (! ret) {\r
+    if (! GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0)) {\r
+      log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GENERATECONSOLECTRLEVENT_FAILED, k->name, error_string(GetLastError()), 0);\r
+      ret = 5;\r
+    }\r
+  }\r
+\r
+  /* Detach from the console. */\r
+  if (! FreeConsole()) {\r
+    log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_FREECONSOLE_FAILED, k->name, error_string(GetLastError()), 0);\r
+  }\r
+\r
+  /* Wait for process to exit. */\r
+  if (await_single_handle(k->status_handle, k->status, k->process_handle, k->name, _T(__FUNCTION__), k->kill_console_delay)) ret = 6;\r
+\r
+  return ret;\r
+}\r
+\r
+int kill_console(kill_t *k) {\r
+  return kill_console(NULL, k);\r
+}\r
+\r
+void kill_process_tree(nssm_service_t * service, kill_t *k, unsigned long ppid) {\r
+  if (! k) return;\r
+  /* Shouldn't happen unless the service failed to start. */\r
+  if (! k->pid) return; /* XXX: needed? */\r
+  unsigned long pid = k->pid;\r
+\r
+  TCHAR pid_string[16], code[16];\r
+  _sntprintf_s(pid_string, _countof(pid_string), _TRUNCATE, _T("%lu"), pid);\r
+  _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), k->exitcode);\r
+  log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_KILLING, k->name, pid_string, code, 0);\r
+\r
+  /* We will need a process handle in order to call TerminateProcess() later. */\r
+  HANDLE process_handle = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_TERMINATE, false, pid);\r
+  if (process_handle) {\r
+    /* Kill this process first, then its descendents. */\r
+    TCHAR ppid_string[16];\r
+    _sntprintf_s(ppid_string, _countof(ppid_string), _TRUNCATE, _T("%lu"), ppid);\r
+    log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_KILL_PROCESS_TREE, pid_string, ppid_string, k->name, 0);\r
+    k->process_handle = process_handle; /* XXX: open directly? */\r
+    if (! kill_process(k)) {\r
+      /* Maybe it already died. */\r
+      unsigned long ret;\r
+      if (! GetExitCodeProcess(process_handle, &ret) || ret == STILL_ACTIVE) {\r
+        if (k->stop_method & NSSM_STOP_METHOD_TERMINATE) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_TERMINATEPROCESS_FAILED, pid_string, k->name, error_string(GetLastError()), 0);\r
+        else log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_PROCESS_STILL_ACTIVE, k->name, pid_string, NSSM, NSSM_REG_STOP_METHOD_SKIP, 0);\r
+      }\r
+    }\r
+\r
+    CloseHandle(process_handle);\r
+  }\r
+  else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENPROCESS_FAILED, pid_string, k->name, error_string(GetLastError()), 0);\r
+\r
+  /* Get a snapshot of all processes in the system. */\r
+  HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);\r
+  if (! snapshot) {\r
+    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETOOLHELP32SNAPSHOT_PROCESS_FAILED, k->name, error_string(GetLastError()), 0);\r
+    return;\r
+  }\r
+\r
+  PROCESSENTRY32 pe;\r
+  ZeroMemory(&pe, sizeof(pe));\r
+  pe.dwSize = sizeof(pe);\r
+\r
+  if (! Process32First(snapshot, &pe)) {\r
+    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_PROCESS_ENUMERATE_FAILED, k->name, error_string(GetLastError()), 0);\r
+    CloseHandle(snapshot);\r
+    return;\r
+  }\r
+\r
+  /* This is a child of the doomed process so kill it. */\r
+  if (! check_parent(k, &pe, pid)) {\r
+    k->pid = pe.th32ProcessID;\r
+    kill_process_tree(k, ppid);\r
+  }\r
+  k->pid = pid;\r
+\r
+  while (true) {\r
+    /* Try to get the next process. */\r
+    if (! Process32Next(snapshot, &pe)) {\r
+      unsigned long ret = GetLastError();\r
+      if (ret == ERROR_NO_MORE_FILES) break;\r
+      log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_PROCESS_ENUMERATE_FAILED, k->name, error_string(GetLastError()), 0);\r
+      CloseHandle(snapshot);\r
+      return;\r
+    }\r
+\r
+    if (! check_parent(k, &pe, pid)) {\r
+      k->pid = pe.th32ProcessID;\r
+      kill_process_tree(k, ppid);\r
+    }\r
+    k->pid = pid;\r
+  }\r
+\r
+  CloseHandle(snapshot);\r
+}\r
+\r
+void kill_process_tree(kill_t *k, unsigned long ppid) {\r
+  return kill_process_tree(NULL, k, ppid);\r
+}\r
index 2e402e4..1bfc273 100644 (file)
--- a/process.h
+++ b/process.h
@@ -1,36 +1,36 @@
-#ifndef PROCESS_H
-#define PROCESS_H
-
-#include <tlhelp32.h>
-
-typedef struct {
-  TCHAR *name;
-  HANDLE process_handle;
-  unsigned long pid;
-  unsigned long exitcode;
-  unsigned long stop_method;
-  unsigned long kill_console_delay;
-  unsigned long kill_window_delay;
-  unsigned long kill_threads_delay;
-  SERVICE_STATUS_HANDLE status_handle;
-  SERVICE_STATUS *status;
-  FILETIME creation_time;
-  FILETIME exit_time;
-  int signalled;
-} kill_t;
-
-void service_kill_t(nssm_service_t *, kill_t *);
-int get_process_creation_time(HANDLE, FILETIME *);
-int get_process_exit_time(HANDLE, FILETIME *);
-int check_parent(kill_t *, PROCESSENTRY32 *, unsigned long);
-int CALLBACK kill_window(HWND, LPARAM);
-int kill_threads(nssm_service_t *, kill_t *);
-int kill_threads(kill_t *);
-int kill_console(nssm_service_t *, kill_t *);
-int kill_console(kill_t *);
-int kill_process(nssm_service_t *, kill_t *);
-int kill_process(kill_t *);
-void kill_process_tree(nssm_service_t *, kill_t *, unsigned long);
-void kill_process_tree(kill_t *, unsigned long);
-
-#endif
+#ifndef PROCESS_H\r
+#define PROCESS_H\r
+\r
+#include <tlhelp32.h>\r
+\r
+typedef struct {\r
+  TCHAR *name;\r
+  HANDLE process_handle;\r
+  unsigned long pid;\r
+  unsigned long exitcode;\r
+  unsigned long stop_method;\r
+  unsigned long kill_console_delay;\r
+  unsigned long kill_window_delay;\r
+  unsigned long kill_threads_delay;\r
+  SERVICE_STATUS_HANDLE status_handle;\r
+  SERVICE_STATUS *status;\r
+  FILETIME creation_time;\r
+  FILETIME exit_time;\r
+  int signalled;\r
+} kill_t;\r
+\r
+void service_kill_t(nssm_service_t *, kill_t *);\r
+int get_process_creation_time(HANDLE, FILETIME *);\r
+int get_process_exit_time(HANDLE, FILETIME *);\r
+int check_parent(kill_t *, PROCESSENTRY32 *, unsigned long);\r
+int CALLBACK kill_window(HWND, LPARAM);\r
+int kill_threads(nssm_service_t *, kill_t *);\r
+int kill_threads(kill_t *);\r
+int kill_console(nssm_service_t *, kill_t *);\r
+int kill_console(kill_t *);\r
+int kill_process(nssm_service_t *, kill_t *);\r
+int kill_process(kill_t *);\r
+void kill_process_tree(nssm_service_t *, kill_t *, unsigned long);\r
+void kill_process_tree(kill_t *, unsigned long);\r
+\r
+#endif\r
index 4bbdd57..45e0a58 100644 (file)
-#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 == PtrToUlong(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 inline bool split_hook_name(const TCHAR *hook_name, TCHAR *hook_event, TCHAR *hook_action) {
-  TCHAR *s;
-
-  for (s = (TCHAR *) hook_name; *s; s++) {
-    if (*s == _T('/')) {
-      *s = _T('\0');
-      _sntprintf_s(hook_event, HOOK_NAME_LENGTH, _TRUNCATE, _T("%s"), hook_name);
-      _sntprintf_s(hook_action, HOOK_NAME_LENGTH, _TRUNCATE, _T("%s"), ++s);
-      return valid_hook_name(hook_event, hook_action, false);
-    }
-  }
-
-  print_message(stderr, NSSM_MESSAGE_INVALID_HOOK_NAME, hook_name);
-  return false;
-}
-
-static int setting_set_hook(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
-  TCHAR hook_event[HOOK_NAME_LENGTH];
-  TCHAR hook_action[HOOK_NAME_LENGTH];
-  if (! split_hook_name(additional, hook_event, hook_action)) return -1;
-
-  TCHAR *cmd;
-  if (value && value->string) cmd = value->string;
-  else cmd = _T("");
-
-  if (set_hook(service_name, hook_event, hook_action, cmd)) return -1;
-  if (! _tcslen(cmd)) return 0;
-  return 1;
-}
-
-static int setting_get_hook(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
-  TCHAR hook_event[HOOK_NAME_LENGTH];
-  TCHAR hook_action[HOOK_NAME_LENGTH];
-  if (! split_hook_name(additional, hook_event, hook_action)) return -1;
-
-  TCHAR cmd[CMD_LENGTH];
-  if (get_hook(service_name, hook_event, hook_action, cmd, sizeof(cmd))) return -1;
-
-  value_from_string(name, value, cmd);
-
-  if (! _tcslen(cmd)) 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 <additional>=<string> NULL NULL */
-      if (! _tcsnicmp(s, additional, len) && s[len] == _T('=')) {
-        /* Strip <key>= */
-        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_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
-  HKEY key = open_service_registry(service_name, KEY_SET_VALUE, false);
-  if (! key) return -1;
-
-  int ret = setting_set_environment(service_name, (void *) key, NSSM_NATIVE_ENVIRONMENT, default_value, value, additional);
-  RegCloseKey(key);
-  return ret;
-}
-
-int native_get_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
-  HKEY key = open_service_registry(service_name, KEY_READ, false);
-  if (! key) return -1;
-
-  ZeroMemory(value, sizeof(value_t));
-  int ret = setting_get_environment(service_name, (void *) key, NSSM_NATIVE_ENVIRONMENT, default_value, value, additional);
-  RegCloseKey(key);
-  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 <service> ObjectName <username> <password>
-    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 = PtrToUlong(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_HOOK, REG_SZ, (void *) _T(""), false, ADDITIONAL_MANDATORY, setting_set_hook, setting_get_hook },
-  { 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_STDOUT NSSM_REG_STDIO_COPY_AND_TRUNCATE, REG_DWORD, 0, 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_STDERR NSSM_REG_STDIO_COPY_AND_TRUNCATE, REG_DWORD, 0, 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_KILL_PROCESS_TREE, REG_DWORD, (void *) 1, 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_REG_ROTATE_DELAY, REG_DWORD, (void *) NSSM_ROTATE_DELAY, 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_ENVIRONMENT, REG_MULTI_SZ, NULL, true, ADDITIONAL_CRLF, native_set_environment, native_get_environment },
-  { 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 }
-};
+#include "nssm.h"\r
+/* XXX: (value && value->string) is probably bogus because value is probably never null */\r
+\r
+/* Affinity. */\r
+#define NSSM_AFFINITY_ALL _T("All")\r
+\r
+extern const TCHAR *exit_action_strings[];\r
+extern const TCHAR *startup_strings[];\r
+extern const TCHAR *priority_strings[];\r
+\r
+/* Does the parameter refer to the default value of the setting? */\r
+static inline int is_default(const TCHAR *value) {\r
+  return (str_equiv(value, _T("default")) || str_equiv(value, _T("*")) || ! value[0]);\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
+    value->string = 0;\r
+    return 0;\r
+  }\r
+\r
+  value->string = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR));\r
+  if (! value->string) {\r
+    print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, name, _T("value_from_string()"));\r
+    return -1;\r
+  }\r
+\r
+  if (_sntprintf_s(value->string, len, _TRUNCATE, _T("%s"), string) < 0) {\r
+    HeapFree(GetProcessHeap(), 0, value->string);\r
+    print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, name, _T("value_from_string()"));\r
+    return -1;\r
+  }\r
+\r
+  return 1;\r
+}\r
+\r
+/* Functions to manage NSSM-specific settings in the registry. */\r
+static int setting_set_number(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
+\r
+  unsigned long number;\r
+  long error;\r
+\r
+  /* Resetting to default? */\r
+  if (! value || ! value->string) {\r
+    error = RegDeleteValue(key, name);\r
+    if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;\r
+    print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));\r
+    return -1;\r
+  }\r
+  if (str_number(value->string, &number)) return -1;\r
+\r
+  if (default_value && number == PtrToUlong(default_value)) {\r
+    error = RegDeleteValue(key, name);\r
+    if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;\r
+    print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));\r
+    return -1;\r
+  }\r
+\r
+  if (set_number(key, (TCHAR *) name, number)) return -1;\r
+\r
+  return 1;\r
+}\r
+\r
+static int setting_get_number(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
+  HKEY key = (HKEY) param;\r
+  return get_number(key, (TCHAR *) name, &value->numeric, false);\r
+}\r
+\r
+static int setting_set_string(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
+\r
+  long error;\r
+\r
+  /* Resetting to default? */\r
+  if (! value || ! value->string) {\r
+    if (default_value) value->string = (TCHAR *) default_value;\r
+    else {\r
+      error = RegDeleteValue(key, name);\r
+      if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;\r
+      print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));\r
+      return -1;\r
+    }\r
+  }\r
+  if (default_value && _tcslen((TCHAR *) default_value) && str_equiv(value->string, (TCHAR *) default_value)) {\r
+    error = RegDeleteValue(key, name);\r
+    if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;\r
+    print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));\r
+    return -1;\r
+  }\r
+\r
+  if (set_expand_string(key, (TCHAR *) name, value->string)) return -1;\r
+\r
+  return 1;\r
+}\r
+\r
+static int setting_get_string(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
+  HKEY key = (HKEY) param;\r
+  TCHAR buffer[VALUE_LENGTH];\r
+\r
+  if (get_string(key, (TCHAR *) name, (TCHAR *) buffer, (unsigned long) sizeof(buffer), false, false, false)) return -1;\r
+\r
+  return value_from_string(name, value, buffer);\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
+  TCHAR action_string[ACTION_LEN];\r
+\r
+  if (additional) {\r
+    /* Default action? */\r
+    if (is_default(additional)) code = 0;\r
+    else {\r
+      if (str_number(additional, &exitcode)) return -1;\r
+      code = (TCHAR *) additional;\r
+    }\r
+  }\r
+\r
+  HKEY key = open_registry(service_name, name, KEY_WRITE);\r
+  if (! key) return -1;\r
+\r
+  long error;\r
+  int ret = 1;\r
+\r
+  /* Resetting to default? */\r
+  if (value && value->string) _sntprintf_s(action_string, _countof(action_string), _TRUNCATE, _T("%s"), value->string);\r
+  else {\r
+    if (code) {\r
+      /* Delete explicit action. */\r
+      error = RegDeleteValue(key, code);\r
+      RegCloseKey(key);\r
+      if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;\r
+      print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, code, service_name, error_string(error));\r
+      return -1;\r
+    }\r
+    else {\r
+      /* Explicitly keep the default action. */\r
+      if (default_value) _sntprintf_s(action_string, _countof(action_string), _TRUNCATE, _T("%s"), (TCHAR *) default_value);\r
+      ret = 0;\r
+    }\r
+  }\r
+\r
+  /* Validate the string. */\r
+  for (int i = 0; exit_action_strings[i]; i++) {\r
+    if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {\r
+      if (default_value && str_equiv(action_string, (TCHAR *) default_value)) ret = 0;\r
+      if (RegSetValueEx(key, code, 0, REG_SZ, (const unsigned char *) exit_action_strings[i], (unsigned long) (_tcslen(action_string) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) {\r
+        print_message(stderr, NSSM_MESSAGE_SETVALUE_FAILED, code, service_name, error_string(GetLastError()));\r
+        RegCloseKey(key);\r
+        return -1;\r
+      }\r
+\r
+      RegCloseKey(key);\r
+      return ret;\r
+    }\r
+  }\r
+\r
+  print_message(stderr, NSSM_MESSAGE_INVALID_EXIT_ACTION, action_string);\r
+  for (int i = 0; exit_action_strings[i]; i++) _ftprintf(stderr, _T("%s\n"), exit_action_strings[i]);\r
+\r
+  return -1;\r
+}\r
+\r
+static int setting_get_exit_action(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
+  unsigned long exitcode = 0;\r
+  unsigned long *code = 0;\r
+\r
+  if (additional) {\r
+    if (! is_default(additional)) {\r
+      if (str_number(additional, &exitcode)) return -1;\r
+      code = &exitcode;\r
+    }\r
+  }\r
+\r
+  TCHAR action_string[ACTION_LEN];\r
+  bool default_action;\r
+  if (get_exit_action(service_name, code, action_string, &default_action)) return -1;\r
+\r
+  value_from_string(name, value, action_string);\r
+\r
+  if (default_action && ! _tcsnicmp((const TCHAR *) action_string, (TCHAR *) default_value, ACTION_LEN)) return 0;\r
+  return 1;\r
+}\r
+\r
+static inline bool split_hook_name(const TCHAR *hook_name, TCHAR *hook_event, TCHAR *hook_action) {\r
+  TCHAR *s;\r
+\r
+  for (s = (TCHAR *) hook_name; *s; s++) {\r
+    if (*s == _T('/')) {\r
+      *s = _T('\0');\r
+      _sntprintf_s(hook_event, HOOK_NAME_LENGTH, _TRUNCATE, _T("%s"), hook_name);\r
+      _sntprintf_s(hook_action, HOOK_NAME_LENGTH, _TRUNCATE, _T("%s"), ++s);\r
+      return valid_hook_name(hook_event, hook_action, false);\r
+    }\r
+  }\r
+\r
+  print_message(stderr, NSSM_MESSAGE_INVALID_HOOK_NAME, hook_name);\r
+  return false;\r
+}\r
+\r
+static int setting_set_hook(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
+  TCHAR hook_event[HOOK_NAME_LENGTH];\r
+  TCHAR hook_action[HOOK_NAME_LENGTH];\r
+  if (! split_hook_name(additional, hook_event, hook_action)) return -1;\r
+\r
+  TCHAR *cmd;\r
+  if (value && value->string) cmd = value->string;\r
+  else cmd = _T("");\r
+\r
+  if (set_hook(service_name, hook_event, hook_action, cmd)) return -1;\r
+  if (! _tcslen(cmd)) return 0;\r
+  return 1;\r
+}\r
+\r
+static int setting_get_hook(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
+  TCHAR hook_event[HOOK_NAME_LENGTH];\r
+  TCHAR hook_action[HOOK_NAME_LENGTH];\r
+  if (! split_hook_name(additional, hook_event, hook_action)) return -1;\r
+\r
+  TCHAR cmd[CMD_LENGTH];\r
+  if (get_hook(service_name, hook_event, hook_action, cmd, sizeof(cmd))) return -1;\r
+\r
+  value_from_string(name, value, cmd);\r
+\r
+  if (! _tcslen(cmd)) return 0;\r
+  return 1;\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
+\r
+  long error;\r
+  __int64 mask;\r
+  __int64 system_affinity = 0LL;\r
+\r
+  if (value && value->string) {\r
+    DWORD_PTR affinity;\r
+    if (! GetProcessAffinityMask(GetCurrentProcess(), &affinity, (DWORD_PTR *) &system_affinity)) system_affinity = ~0;\r
+\r
+    if (is_default(value->string) || str_equiv(value->string, NSSM_AFFINITY_ALL)) mask = 0LL;\r
+    else if (affinity_string_to_mask(value->string, &mask)) {\r
+      print_message(stderr, NSSM_MESSAGE_BOGUS_AFFINITY_MASK, value->string, num_cpus() - 1);\r
+      return -1;\r
+    }\r
+  }\r
+  else mask = 0LL;\r
+\r
+  if (! mask) {\r
+    error = RegDeleteValue(key, name);\r
+    if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;\r
+    print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));\r
+    return -1;\r
+  }\r
+\r
+  /* Canonicalise. */\r
+  TCHAR *canon = 0;\r
+  if (affinity_mask_to_string(mask, &canon)) canon = value->string;\r
+\r
+  __int64 effective_affinity = mask & system_affinity;\r
+  if (effective_affinity != mask) {\r
+    /* Requested CPUs did not intersect with available CPUs? */\r
+    if (! effective_affinity) mask = effective_affinity = system_affinity;\r
+\r
+    TCHAR *system = 0;\r
+    if (! affinity_mask_to_string(system_affinity, &system)) {\r
+      TCHAR *effective = 0;\r
+      if (! affinity_mask_to_string(effective_affinity, &effective)) {\r
+        print_message(stderr, NSSM_MESSAGE_EFFECTIVE_AFFINITY_MASK, value->string, system, effective);\r
+        HeapFree(GetProcessHeap(), 0, effective);\r
+      }\r
+      HeapFree(GetProcessHeap(), 0, system);\r
+    }\r
+  }\r
+\r
+  if (RegSetValueEx(key, name, 0, REG_SZ, (const unsigned char *) canon, (unsigned long) (_tcslen(canon) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) {\r
+    if (canon != value->string) HeapFree(GetProcessHeap(), 0, canon);\r
+    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, name, error_string(GetLastError()), 0);\r
+    return -1;\r
+  }\r
+\r
+  if (canon != value->string) HeapFree(GetProcessHeap(), 0, canon);\r
+  return 1;\r
+}\r
+\r
+static int setting_get_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
+\r
+  unsigned long type;\r
+  TCHAR *buffer = 0;\r
+  unsigned long buflen = 0;\r
+\r
+  int ret = RegQueryValueEx(key, name, 0, &type, 0, &buflen);\r
+  if (ret == ERROR_FILE_NOT_FOUND) {\r
+    if (value_from_string(name, value, NSSM_AFFINITY_ALL) == 1) return 0;\r
+    return -1;\r
+  }\r
+  if (ret != ERROR_SUCCESS) return -1;\r
+\r
+  if (type != REG_SZ) return -1;\r
+\r
+  buffer = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, buflen);\r
+  if (! buffer) {\r
+    print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("affinity"), _T("setting_get_affinity"));\r
+    return -1;\r
+  }\r
+\r
+  if (get_string(key, (TCHAR *) name, buffer, buflen, false, false, true)) {\r
+    HeapFree(GetProcessHeap(), 0, buffer);\r
+    return -1;\r
+  }\r
+\r
+  __int64 affinity;\r
+  if (affinity_string_to_mask(buffer, &affinity)) {\r
+    print_message(stderr, NSSM_MESSAGE_BOGUS_AFFINITY_MASK, buffer, num_cpus() - 1);\r
+    HeapFree(GetProcessHeap(), 0, buffer);\r
+    return -1;\r
+  }\r
+\r
+  HeapFree(GetProcessHeap(), 0, buffer);\r
+\r
+  /* Canonicalise. */\r
+  if (affinity_mask_to_string(affinity, &buffer)) {\r
+    if (buffer) HeapFree(GetProcessHeap(), 0, buffer);\r
+    return -1;\r
+  }\r
+\r
+  ret = value_from_string(name, value, buffer);\r
+  HeapFree(GetProcessHeap(), 0, buffer);\r
+  return ret;\r
+}\r
+\r
+static int setting_set_environment(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
+\r
+  if (! value || ! value->string || ! value->string[0]) {\r
+    long error = RegDeleteValue(key, name);\r
+    if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;\r
+    print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));\r
+    return -1;\r
+  }\r
+\r
+  unsigned long envlen = (unsigned long) _tcslen(value->string) + 1;\r
+  TCHAR *unformatted = 0;\r
+  unsigned long newlen;\r
+  if (unformat_double_null(value->string, envlen, &unformatted, &newlen)) return -1;\r
+\r
+  if (test_environment(unformatted)) {\r
+    HeapFree(GetProcessHeap(), 0, unformatted);\r
+    print_message(stderr, NSSM_GUI_INVALID_ENVIRONMENT);\r
+    return -1;\r
+  }\r
+\r
+  if (RegSetValueEx(key, name, 0, REG_MULTI_SZ, (const unsigned char *) unformatted, (unsigned long) newlen * sizeof(TCHAR)) != ERROR_SUCCESS) {\r
+    if (newlen) HeapFree(GetProcessHeap(), 0, unformatted);\r
+    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV, error_string(GetLastError()), 0);\r
+    return -1;\r
+  }\r
+\r
+  if (newlen) HeapFree(GetProcessHeap(), 0, unformatted);\r
+  return 1;\r
+}\r
+\r
+static int setting_get_environment(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
+\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 *formatted;\r
+  unsigned long newlen;\r
+  if (format_double_null(env, envlen, &formatted, &newlen)) return -1;\r
+\r
+  int ret;\r
+  if (additional) {\r
+    /* Find named environment variable. */\r
+    TCHAR *s;\r
+    size_t len = _tcslen(additional);\r
+    for (s = env; *s; s++) {\r
+      /* Look for <additional>=<string> NULL NULL */\r
+      if (! _tcsnicmp(s, additional, len) && s[len] == _T('=')) {\r
+        /* Strip <key>= */\r
+        s += len + 1;\r
+        ret = value_from_string(name, value, s);\r
+        HeapFree(GetProcessHeap(), 0, env);\r
+        return ret;\r
+      }\r
+\r
+      /* Skip this string. */\r
+      for ( ; *s; s++);\r
+    }\r
+    HeapFree(GetProcessHeap(), 0, env);\r
+    return 0;\r
+  }\r
+\r
+  HeapFree(GetProcessHeap(), 0, env);\r
+\r
+  ret = value_from_string(name, value, formatted);\r
+  if (newlen) HeapFree(GetProcessHeap(), 0, formatted);\r
+  return ret;\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
+\r
+  TCHAR *priority_string;\r
+  int i;\r
+  long error;\r
+\r
+  if (value && value->string) priority_string = value->string;\r
+  else if (default_value) priority_string = (TCHAR *) default_value;\r
+  else {\r
+    error = RegDeleteValue(key, name);\r
+    if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;\r
+    print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));\r
+    return -1;\r
+  }\r
+\r
+  for (i = 0; priority_strings[i]; i++) {\r
+    if (! str_equiv(priority_strings[i], priority_string)) continue;\r
+\r
+    if (default_value && str_equiv(priority_string, (TCHAR *) default_value)) {\r
+      error = RegDeleteValue(key, name);\r
+      if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;\r
+      print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));\r
+      return -1;\r
+    }\r
+\r
+    if (set_number(key, (TCHAR *) name, priority_index_to_constant(i))) return -1;\r
+    return 1;\r
+  }\r
+\r
+  print_message(stderr, NSSM_MESSAGE_INVALID_PRIORITY, priority_string);\r
+  for (i = 0; priority_strings[i]; i++) _ftprintf(stderr, _T("%s\n"), priority_strings[i]);\r
+\r
+  return -1;\r
+}\r
+\r
+static int setting_get_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
+\r
+  unsigned long constant;\r
+  switch (get_number(key, (TCHAR *) name, &constant, false)) {\r
+    case 0: return value_from_string(name, value, (const TCHAR *) default_value);\r
+    case -1: return -1;\r
+  }\r
+\r
+  return value_from_string(name, value, priority_strings[priority_constant_to_index(constant)]);\r
+}\r
+\r
+/* Functions to manage native service settings. */\r
+static int native_set_dependongroup(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
+\r
+  /*\r
+    Get existing service dependencies because we must set both types together.\r
+  */\r
+  TCHAR *buffer;\r
+  unsigned long buflen;\r
+  if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, DEPENDENCY_SERVICES)) return -1;\r
+\r
+  if (! value || ! value->string || ! value->string[0]) {\r
+    if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, buffer, 0, 0, 0)) {\r
+      print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));\r
+      if (buffer) HeapFree(GetProcessHeap(), 0, buffer);\r
+      return -1;\r
+    }\r
+\r
+    if (buffer) HeapFree(GetProcessHeap(), 0, buffer);\r
+    return 0;\r
+  }\r
+\r
+  unsigned long len = (unsigned long) _tcslen(value->string) + 1;\r
+  TCHAR *unformatted = 0;\r
+  unsigned long newlen;\r
+  if (unformat_double_null(value->string, len, &unformatted, &newlen)) {\r
+    if (buffer) HeapFree(GetProcessHeap(), 0, buffer);\r
+    return -1;\r
+  }\r
+\r
+  /* Prepend group identifier. */\r
+  unsigned long missing = 0;\r
+  TCHAR *canon = unformatted;\r
+  size_t canonlen = 0;\r
+  TCHAR *s;\r
+  for (s = unformatted; *s; s++) {\r
+    if (*s != SC_GROUP_IDENTIFIER) missing++;\r
+    size_t len = _tcslen(s);\r
+    canonlen += len + 1;\r
+    s += len;\r
+  }\r
+\r
+  if (missing) {\r
+    /* Missing identifiers plus double NULL terminator. */\r
+    canonlen += missing + 1;\r
+    newlen = (unsigned long) canonlen;\r
+\r
+    canon = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, canonlen * sizeof(TCHAR));\r
+    if (! canon) {\r
+      print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canon"), _T("native_set_dependongroup"));\r
+      if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);\r
+      if (buffer) HeapFree(GetProcessHeap(), 0, buffer);\r
+      return -1;\r
+    }\r
+\r
+    size_t i = 0;\r
+    for (s = unformatted; *s; s++) {\r
+      if (*s != SC_GROUP_IDENTIFIER) canon[i++] = SC_GROUP_IDENTIFIER;\r
+      size_t len = _tcslen(s);\r
+      memmove(canon + i, s, (len + 1) * sizeof(TCHAR));\r
+      i += len + 1;\r
+      s += len;\r
+    }\r
+  }\r
+\r
+  TCHAR *dependencies;\r
+  if (buflen > 2) {\r
+    dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (newlen + buflen) * sizeof(TCHAR));\r
+    if (! dependencies) {\r
+      print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("dependencies"), _T("native_set_dependongroup"));\r
+      if (canon != unformatted) HeapFree(GetProcessHeap(), 0, canon);\r
+      if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);\r
+      if (buffer) HeapFree(GetProcessHeap(), 0, buffer);\r
+      return -1;\r
+    }\r
+\r
+    memmove(dependencies, buffer, buflen * sizeof(TCHAR));\r
+    memmove(dependencies + buflen - 1, canon, newlen * sizeof(TCHAR));\r
+  }\r
+  else dependencies = canon;\r
+\r
+  int ret = 1;\r
+  if (set_service_dependencies(service_name, service_handle, dependencies)) ret = -1;\r
+  if (dependencies != unformatted) HeapFree(GetProcessHeap(), 0, dependencies);\r
+  if (canon != unformatted) HeapFree(GetProcessHeap(), 0, canon);\r
+  if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);\r
+  if (buffer) HeapFree(GetProcessHeap(), 0, buffer);\r
+\r
+  return ret;\r
+}\r
+\r
+static int native_get_dependongroup(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
+\r
+  TCHAR *buffer;\r
+  unsigned long buflen;\r
+  if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, DEPENDENCY_GROUPS)) return -1;\r
+\r
+  int ret;\r
+  if (buflen) {\r
+    TCHAR *formatted;\r
+    unsigned long newlen;\r
+    if (format_double_null(buffer, buflen, &formatted, &newlen)) {\r
+      HeapFree(GetProcessHeap(), 0, buffer);\r
+      return -1;\r
+    }\r
+\r
+    ret = value_from_string(name, value, formatted);\r
+    HeapFree(GetProcessHeap(), 0, formatted);\r
+    HeapFree(GetProcessHeap(), 0, buffer);\r
+  }\r
+  else {\r
+    value->string = 0;\r
+    ret = 0;\r
+  }\r
+\r
+  return ret;\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
+\r
+  /*\r
+    Get existing group dependencies because we must set both types together.\r
+  */\r
+  TCHAR *buffer;\r
+  unsigned long buflen;\r
+  if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, DEPENDENCY_GROUPS)) return -1;\r
+\r
+  if (! value || ! value->string || ! value->string[0]) {\r
+    if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, buffer, 0, 0, 0)) {\r
+      print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));\r
+      if (buffer) HeapFree(GetProcessHeap(), 0, buffer);\r
+      return -1;\r
+    }\r
+\r
+    if (buffer) HeapFree(GetProcessHeap(), 0, buffer);\r
+    return 0;\r
+  }\r
+\r
+  unsigned long len = (unsigned long) _tcslen(value->string) + 1;\r
+  TCHAR *unformatted = 0;\r
+  unsigned long newlen;\r
+  if (unformat_double_null(value->string, len, &unformatted, &newlen)) {\r
+    if (buffer) HeapFree(GetProcessHeap(), 0, buffer);\r
+    return -1;\r
+  }\r
+\r
+  TCHAR *dependencies;\r
+  if (buflen > 2) {\r
+    dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (newlen + buflen) * sizeof(TCHAR));\r
+    if (! dependencies) {\r
+      print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("dependencies"), _T("native_set_dependonservice"));\r
+      if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);\r
+      if (buffer) HeapFree(GetProcessHeap(), 0, buffer);\r
+      return -1;\r
+    }\r
+\r
+    memmove(dependencies, buffer, buflen * sizeof(TCHAR));\r
+    memmove(dependencies + buflen - 1, unformatted, newlen * sizeof(TCHAR));\r
+  }\r
+  else dependencies = unformatted;\r
+\r
+  int ret = 1;\r
+  if (set_service_dependencies(service_name, service_handle, dependencies)) ret = -1;\r
+  if (dependencies != unformatted) HeapFree(GetProcessHeap(), 0, dependencies);\r
+  if (unformatted) HeapFree(GetProcessHeap(), 0, unformatted);\r
+  if (buffer) HeapFree(GetProcessHeap(), 0, buffer);\r
+\r
+  return ret;\r
+}\r
+\r
+static int native_get_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
+\r
+  TCHAR *buffer;\r
+  unsigned long buflen;\r
+  if (get_service_dependencies(service_name, service_handle, &buffer, &buflen, DEPENDENCY_SERVICES)) return -1;\r
+\r
+  int ret;\r
+  if (buflen) {\r
+    TCHAR *formatted;\r
+    unsigned long newlen;\r
+    if (format_double_null(buffer, buflen, &formatted, &newlen)) {\r
+      HeapFree(GetProcessHeap(), 0, buffer);\r
+      return -1;\r
+    }\r
+\r
+    ret = value_from_string(name, value, formatted);\r
+    HeapFree(GetProcessHeap(), 0, formatted);\r
+    HeapFree(GetProcessHeap(), 0, buffer);\r
+  }\r
+  else {\r
+    value->string = 0;\r
+    ret = 0;\r
+  }\r
+\r
+  return ret;\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
+\r
+  TCHAR *description = 0;\r
+  if (value) description = value->string;\r
+  if (set_service_description(service_name, service_handle, description)) return -1;\r
+\r
+  if (description && description[0]) return 1;\r
+\r
+  return 0;\r
+}\r
+\r
+int native_get_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
+\r
+  TCHAR buffer[VALUE_LENGTH];\r
+  if (get_service_description(service_name, service_handle, _countof(buffer), buffer)) return -1;\r
+\r
+  if (buffer[0]) return value_from_string(name, value, buffer);\r
+  value->string = 0;\r
+\r
+  return 0;\r
+}\r
+\r
+int native_set_displayname(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
+\r
+  TCHAR *displayname = 0;\r
+  if (value && value->string) displayname = value->string;\r
+  else displayname = (TCHAR *) service_name;\r
+\r
+  if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, displayname)) {\r
+    print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));\r
+    return -1;\r
+  }\r
+\r
+  /*\r
+    If the display name and service name differ only in case,\r
+    ChangeServiceConfig() will return success but the display name will be\r
+    set to the service name, NOT the value passed to the function.\r
+    This appears to be a quirk of Windows rather than a bug here.\r
+  */\r
+  if (displayname != service_name && ! str_equiv(displayname, service_name)) return 1;\r
+\r
+  return 0;\r
+}\r
+\r
+int native_get_displayname(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
+\r
+  QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);\r
+  if (! qsc) return -1;\r
+\r
+  int ret = value_from_string(name, value, qsc->lpDisplayName);\r
+  HeapFree(GetProcessHeap(), 0, qsc);\r
+\r
+  return ret;\r
+}\r
+\r
+int native_set_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_SET_VALUE, false);\r
+  if (! key) return -1;\r
+\r
+  int ret = setting_set_environment(service_name, (void *) key, NSSM_NATIVE_ENVIRONMENT, default_value, value, additional);\r
+  RegCloseKey(key);\r
+  return ret;\r
+}\r
+\r
+int native_get_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, false);\r
+  if (! key) return -1;\r
+\r
+  ZeroMemory(value, sizeof(value_t));\r
+  int ret = setting_get_environment(service_name, (void *) key, NSSM_NATIVE_ENVIRONMENT, 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
+\r
+  /* It makes no sense to try to reset the image path. */\r
+  if (! value || ! value->string) {\r
+    print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);\r
+    return -1;\r
+  }\r
+\r
+  if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, value->string, 0, 0, 0, 0, 0, 0)) {\r
+    print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));\r
+    return -1;\r
+  }\r
+\r
+  return 1;\r
+}\r
+\r
+int native_get_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
+\r
+  QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);\r
+  if (! qsc) return -1;\r
+\r
+  int ret = value_from_string(name, value, qsc->lpBinaryPathName);\r
+  HeapFree(GetProcessHeap(), 0, qsc);\r
+\r
+  return ret;\r
+}\r
+\r
+int native_set_name(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
+  print_message(stderr, NSSM_MESSAGE_CANNOT_RENAME_SERVICE);\r
+  return -1;\r
+}\r
+\r
+int native_get_name(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {\r
+  return value_from_string(name, value, service_name);\r
+}\r
+\r
+int native_set_objectname(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
+\r
+  /*\r
+    Logical syntax is: nssm set <service> ObjectName <username> <password>\r
+    That means the username is actually passed in the additional parameter.\r
+  */\r
+  bool localsystem = false;\r
+  TCHAR *username = NSSM_LOCALSYSTEM_ACCOUNT;\r
+  TCHAR *password = 0;\r
+  if (additional) {\r
+    username = (TCHAR *) additional;\r
+    if (value && value->string) password = value->string;\r
+  }\r
+  else if (value && value->string) username = value->string;\r
+\r
+  const TCHAR *well_known = well_known_username(username);\r
+  size_t passwordsize = 0;\r
+  if (well_known) {\r
+    if (str_equiv(well_known, NSSM_LOCALSYSTEM_ACCOUNT)) localsystem = true;\r
+    username = (TCHAR *) well_known;\r
+    password = _T("");\r
+  }\r
+  else if (! password) {\r
+    /* We need a password if the account requires it. */\r
+    print_message(stderr, NSSM_MESSAGE_MISSING_PASSWORD, name);\r
+    return -1;\r
+  }\r
+  else passwordsize = _tcslen(password) * sizeof(TCHAR);\r
+\r
+  /*\r
+    ChangeServiceConfig() will fail to set the username if the service is set\r
+    to interact with the desktop.\r
+  */\r
+  unsigned long type = SERVICE_NO_CHANGE;\r
+  if (! localsystem) {\r
+    QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);\r
+    if (! qsc) {\r
+      if (passwordsize) SecureZeroMemory(password, passwordsize);\r
+      return -1;\r
+    }\r
+\r
+    type = qsc->dwServiceType & ~SERVICE_INTERACTIVE_PROCESS;\r
+    HeapFree(GetProcessHeap(), 0, qsc);\r
+  }\r
+\r
+  if (! well_known) {\r
+    if (grant_logon_as_service(username)) {\r
+      if (passwordsize) SecureZeroMemory(password, passwordsize);\r
+      print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);\r
+      return -1;\r
+    }\r
+  }\r
+\r
+  if (! ChangeServiceConfig(service_handle, type, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, username, password, 0)) {\r
+    if (passwordsize) SecureZeroMemory(password, passwordsize);\r
+    print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));\r
+    return -1;\r
+  }\r
+\r
+  if (passwordsize) SecureZeroMemory(password, passwordsize);\r
+\r
+  if (localsystem) return 0;\r
+\r
+  return 1;\r
+}\r
+\r
+int native_get_objectname(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
+\r
+  QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);\r
+  if (! qsc) return -1;\r
+\r
+  int ret = value_from_string(name, value, qsc->lpServiceStartName);\r
+  HeapFree(GetProcessHeap(), 0, qsc);\r
+\r
+  return ret;\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
+\r
+  /* It makes no sense to try to reset the startup type. */\r
+  if (! value || ! value->string) {\r
+    print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);\r
+    return -1;\r
+  }\r
+\r
+  /* Map NSSM_STARTUP_* constant to Windows SERVICE_*_START constant. */\r
+  int service_startup = -1;\r
+  int i;\r
+  for (i = 0; startup_strings[i]; i++) {\r
+    if (str_equiv(value->string, startup_strings[i])) {\r
+      service_startup = i;\r
+      break;\r
+    }\r
+  }\r
+\r
+  if (service_startup < 0) {\r
+    print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE_STARTUP, value->string);\r
+    for (i = 0; startup_strings[i]; i++) _ftprintf(stderr, _T("%s\n"), startup_strings[i]);\r
+    return -1;\r
+  }\r
+\r
+  unsigned long startup;\r
+  switch (service_startup) {\r
+    case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break;\r
+    case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break;\r
+    default: startup = SERVICE_AUTO_START;\r
+  }\r
+\r
+  if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, startup, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0)) {\r
+    print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));\r
+    return -1;\r
+  }\r
+\r
+  SERVICE_DELAYED_AUTO_START_INFO delayed;\r
+  ZeroMemory(&delayed, sizeof(delayed));\r
+  if (service_startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;\r
+  else delayed.fDelayedAutostart = 0;\r
+  if (! ChangeServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {\r
+    unsigned long error = GetLastError();\r
+    /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */\r
+    if (error != ERROR_INVALID_LEVEL) {\r
+      log_event(EVENTLOG_ERROR_TYPE, NSSM_MESSAGE_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service_name, error_string(error), 0);\r
+    }\r
+  }\r
+\r
+  return 1;\r
+}\r
+\r
+int native_get_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
+\r
+  QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);\r
+  if (! qsc) return -1;\r
+\r
+  unsigned long startup;\r
+  int ret = get_service_startup(service_name, service_handle, qsc, &startup);\r
+  HeapFree(GetProcessHeap(), 0, qsc);\r
+\r
+  if (ret) return -1;\r
+\r
+  unsigned long i;\r
+  for (i = 0; startup_strings[i]; i++);\r
+  if (startup >= i) return -1;\r
+\r
+  return value_from_string(name, value, startup_strings[startup]);\r
+}\r
+\r
+int native_set_type(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
+\r
+  /* It makes no sense to try to reset the service type. */\r
+  if (! value || ! value->string) {\r
+    print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);\r
+    return -1;\r
+  }\r
+\r
+  /*\r
+    We can only manage services of type SERVICE_WIN32_OWN_PROCESS\r
+    and SERVICE_INTERACTIVE_PROCESS.\r
+  */\r
+  unsigned long type = SERVICE_WIN32_OWN_PROCESS;\r
+  if (str_equiv(value->string, NSSM_INTERACTIVE_PROCESS)) type |= SERVICE_INTERACTIVE_PROCESS;\r
+  else if (! str_equiv(value->string, NSSM_WIN32_OWN_PROCESS)) {\r
+    print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE_TYPE, value->string);\r
+    _ftprintf(stderr, _T("%s\n"), NSSM_WIN32_OWN_PROCESS);\r
+    _ftprintf(stderr, _T("%s\n"), NSSM_INTERACTIVE_PROCESS);\r
+    return -1;\r
+  }\r
+\r
+  /*\r
+    ChangeServiceConfig() will fail if the service runs under an account\r
+    other than LOCALSYSTEM and we try to make it interactive.\r
+  */\r
+  if (type & SERVICE_INTERACTIVE_PROCESS) {\r
+    QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);\r
+    if (! qsc) return -1;\r
+\r
+    if (! str_equiv(qsc->lpServiceStartName, NSSM_LOCALSYSTEM_ACCOUNT)) {\r
+      HeapFree(GetProcessHeap(), 0, qsc);\r
+      print_message(stderr, NSSM_MESSAGE_INTERACTIVE_NOT_LOCALSYSTEM, value->string, service_name, NSSM_LOCALSYSTEM_ACCOUNT);\r
+      return -1;\r
+    }\r
+\r
+    HeapFree(GetProcessHeap(), 0, qsc);\r
+  }\r
+\r
+  if (! ChangeServiceConfig(service_handle, type, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0)) {\r
+    print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));\r
+    return -1;\r
+  }\r
+\r
+  return 1;\r
+}\r
+\r
+int native_get_type(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
+\r
+  QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);\r
+  if (! qsc) return -1;\r
+\r
+  value->numeric = qsc->dwServiceType;\r
+  HeapFree(GetProcessHeap(), 0, qsc);\r
+\r
+  const TCHAR *string;\r
+  switch (value->numeric) {\r
+    case SERVICE_KERNEL_DRIVER: string = NSSM_KERNEL_DRIVER; break;\r
+    case SERVICE_FILE_SYSTEM_DRIVER: string = NSSM_FILE_SYSTEM_DRIVER; break;\r
+    case SERVICE_WIN32_OWN_PROCESS: string = NSSM_WIN32_OWN_PROCESS; break;\r
+    case SERVICE_WIN32_SHARE_PROCESS: string = NSSM_WIN32_SHARE_PROCESS; break;\r
+    case SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS: string = NSSM_INTERACTIVE_PROCESS; break;\r
+    case SERVICE_WIN32_SHARE_PROCESS|SERVICE_INTERACTIVE_PROCESS: string = NSSM_SHARE_INTERACTIVE_PROCESS; break;\r
+    default: string = NSSM_UNKNOWN;\r
+  }\r
+\r
+  return value_from_string(name, value, string);\r
+}\r
+\r
+int set_setting(const TCHAR *service_name, HKEY key, settings_t *setting, value_t *value, const TCHAR *additional) {\r
+  if (! key) return -1;\r
+  int ret;\r
+\r
+  if (setting->set) ret = setting->set(service_name, (void *) key, setting->name, setting->default_value, value, additional);\r
+  else ret = -1;\r
+\r
+  if (! ret) print_message(stdout, NSSM_MESSAGE_RESET_SETTING, setting->name, service_name);\r
+  else if (ret > 0) print_message(stdout, NSSM_MESSAGE_SET_SETTING, setting->name, service_name);\r
+  else print_message(stderr, NSSM_MESSAGE_SET_SETTING_FAILED, setting->name, service_name);\r
+\r
+  return ret;\r
+}\r
+\r
+int set_setting(const TCHAR *service_name, SC_HANDLE service_handle, settings_t *setting, value_t *value, const TCHAR *additional) {\r
+  if (! service_handle) return -1;\r
+\r
+  int ret;\r
+  if (setting->set) ret = setting->set(service_name, service_handle, setting->name, setting->default_value, value, additional);\r
+  else ret = -1;\r
+\r
+  if (! ret) print_message(stdout, NSSM_MESSAGE_RESET_SETTING, setting->name, service_name);\r
+  else if (ret > 0) print_message(stdout, NSSM_MESSAGE_SET_SETTING, setting->name, service_name);\r
+  else print_message(stderr, NSSM_MESSAGE_SET_SETTING_FAILED, setting->name, service_name);\r
+\r
+  return ret;\r
+}\r
+\r
+/*\r
+  Returns:  1 if the value was retrieved.\r
+            0 if the default value was retrieved.\r
+           -1 on error.\r
+*/\r
+int get_setting(const TCHAR *service_name, HKEY key, settings_t *setting, value_t *value, const TCHAR *additional) {\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
+  }\r
+\r
+  if (ret < 0) print_message(stderr, NSSM_MESSAGE_GET_SETTING_FAILED, setting->name, service_name);\r
+\r
+  return ret;\r
+}\r
+\r
+int get_setting(const TCHAR *service_name, SC_HANDLE service_handle, settings_t *setting, value_t *value, const TCHAR *additional) {\r
+  if (! service_handle) return -1;\r
+  return setting->get(service_name, service_handle, setting->name, 0, value, additional);\r
+}\r
+\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_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
+  { NULL, NULL, NULL, NULL, NULL }\r
+};\r
index 2fcffa5..c28df38 100644 (file)
@@ -1,48 +1,48 @@
-#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_ENVIRONMENT _T("Environment")
-#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
+#ifndef SETTINGS_H\r
+#define SETTINGS_H\r
+\r
+#define NSSM_NATIVE_DEPENDONGROUP _T("DependOnGroup")\r
+#define NSSM_NATIVE_DEPENDONSERVICE _T("DependOnService")\r
+#define NSSM_NATIVE_DESCRIPTION _T("Description")\r
+#define NSSM_NATIVE_DISPLAYNAME _T("DisplayName")\r
+#define NSSM_NATIVE_ENVIRONMENT _T("Environment")\r
+#define NSSM_NATIVE_IMAGEPATH _T("ImagePath")\r
+#define NSSM_NATIVE_NAME _T("Name")\r
+#define NSSM_NATIVE_OBJECTNAME _T("ObjectName")\r
+#define NSSM_NATIVE_STARTUP _T("Start")\r
+#define NSSM_NATIVE_TYPE _T("Type")\r
+\r
+/* Are additional arguments needed? */\r
+#define ADDITIONAL_GETTING (1 << 0)\r
+#define ADDITIONAL_SETTING (1 << 1)\r
+#define ADDITIONAL_RESETTING (1 << 2)\r
+#define ADDITIONAL_CRLF (1 << 3)\r
+#define ADDITIONAL_MANDATORY ADDITIONAL_GETTING|ADDITIONAL_SETTING|ADDITIONAL_RESETTING\r
+\r
+#define DEPENDENCY_SERVICES (1 << 0)\r
+#define DEPENDENCY_GROUPS (1 << 1)\r
+#define DEPENDENCY_ALL (DEPENDENCY_SERVICES|DEPENDENCY_GROUPS)\r
+\r
+typedef union {\r
+  unsigned long numeric;\r
+  TCHAR *string;\r
+} value_t;\r
+\r
+typedef int (*setting_function_t)(const TCHAR *, void *, const TCHAR *, void *, value_t *, const TCHAR *);\r
+\r
+typedef struct {\r
+  const TCHAR *name;\r
+  unsigned long type;\r
+  void *default_value;\r
+  bool native;\r
+  int additional;\r
+  setting_function_t set;\r
+  setting_function_t get;\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
+\r
+#endif\r