Browse Source

Use CRLF consistently.

The mismatch of line endings has long been an annoyance.

Thanks Mathias Breiner for advocating finally doing something about it.
Iain Patterson 5 years ago
parent
commit
b6f7fe3b11
15 changed files with 2905 additions and 2905 deletions
  1. 346 346
      account.cpp
  2. 24 24
      account.h
  3. 175 175
      console.cpp
  4. 7 7
      console.h
  5. 181 181
      env.cpp
  6. 14 14
      env.h
  7. 402 402
      hook.cpp
  8. 75 75
      hook.h
  9. 92 92
      imports.cpp
  10. 25 25
      imports.h
  11. BIN
      messages.mc
  12. 350 350
      process.cpp
  13. 36 36
      process.h
  14. 1130 1130
      settings.cpp
  15. 48 48
      settings.h

+ 346 - 346
account.cpp

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

+ 24 - 24
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
+#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

+ 175 - 175
console.cpp

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

+ 7 - 7
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
+#define CONSOLE_H
+
+void check_console();
+void alloc_console(nssm_service_t *);
+
+#endif

+ 181 - 181
env.cpp

@@ -1,181 +1,181 @@
-#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"
+
+/* 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;
+}

+ 14 - 14
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
+#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

+ 402 - 402
hook.cpp

@@ -1,402 +1,402 @@
-#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"
+
+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);
+}

+ 75 - 75
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
+#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

+ 92 - 92
imports.cpp

@@ -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"
+
+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));
+}

+ 25 - 25
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
+#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

BIN
messages.mc


+ 350 - 350
process.cpp

@@ -1,350 +1,350 @@
-#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) {