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

+ 36 - 36
process.h

@@ -1,36 +1,36 @@
-#ifndef PROCESS_H
-#define PROCESS_H
-
-#include <tlhelp32.h>
-
-typedef struct {
-  TCHAR *name;
-  HANDLE process_handle;
-  unsigned long pid;
-  unsigned long exitcode;
-  unsigned long stop_method;
-  unsigned long kill_console_delay;
-  unsigned long kill_window_delay;
-  unsigned long kill_threads_delay;
-  SERVICE_STATUS_HANDLE status_handle;
-  SERVICE_STATUS *status;
-  FILETIME creation_time;
-  FILETIME exit_time;
-  int signalled;
-} kill_t;
-
-void service_kill_t(nssm_service_t *, kill_t *);
-int get_process_creation_time(HANDLE, FILETIME *);
-int get_process_exit_time(HANDLE, FILETIME *);
-int check_parent(kill_t *, PROCESSENTRY32 *, unsigned long);
-int CALLBACK kill_window(HWND, LPARAM);
-int kill_threads(nssm_service_t *, kill_t *);
-int kill_threads(kill_t *);
-int kill_console(nssm_service_t *, kill_t *);
-int kill_console(kill_t *);
-int kill_process(nssm_service_t *, kill_t *);
-int kill_process(kill_t *);
-void kill_process_tree(nssm_service_t *, kill_t *, unsigned long);
-void kill_process_tree(kill_t *, unsigned long);
-
-#endif
+#ifndef PROCESS_H
+#define PROCESS_H
+
+#include <tlhelp32.h>
+
+typedef struct {
+  TCHAR *name;
+  HANDLE process_handle;
+  unsigned long pid;
+  unsigned long exitcode;
+  unsigned long stop_method;
+  unsigned long kill_console_delay;
+  unsigned long kill_window_delay;
+  unsigned long kill_threads_delay;
+  SERVICE_STATUS_HANDLE status_handle;
+  SERVICE_STATUS *status;
+  FILETIME creation_time;
+  FILETIME exit_time;
+  int signalled;
+} kill_t;
+
+void service_kill_t(nssm_service_t *, kill_t *);
+int get_process_creation_time(HANDLE, FILETIME *);
+int get_process_exit_time(HANDLE, FILETIME *);
+int check_parent(kill_t *, PROCESSENTRY32 *, unsigned long);
+int CALLBACK kill_window(HWND, LPARAM);
+int kill_threads(nssm_service_t *, kill_t *);
+int kill_threads(kill_t *);
+int kill_console(nssm_service_t *, kill_t *);
+int kill_console(kill_t *);
+int kill_process(nssm_service_t *, kill_t *);
+int kill_process(kill_t *);
+void kill_process_tree(nssm_service_t *, kill_t *, unsigned long);
+void kill_process_tree(kill_t *, unsigned long);
+
+#endif

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 1130 - 1130
settings.cpp


+ 48 - 48
settings.h

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