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