From: Iain Patterson Date: Sun, 28 Feb 2016 08:59:11 +0000 (+0000) Subject: Use CRLF consistently. X-Git-Url: http://git.iain.cx/?p=nssm.git;a=commitdiff_plain;h=b6f7fe3b11fd130f46eb6b5009391cc3bb0cad49 Use CRLF consistently. The mismatch of line endings has long been an annoyance. Thanks Mathias Breiner for advocating finally doing something about it. --- diff --git a/account.cpp b/account.cpp index 70bfa3e..60d1fc6 100644 --- a/account.cpp +++ b/account.cpp @@ -1,346 +1,346 @@ -#include "nssm.h" - -#include - -extern imports_t imports; - -/* Open Policy object. */ -int open_lsa_policy(LSA_HANDLE *policy) { - LSA_OBJECT_ATTRIBUTES attributes; - ZeroMemory(&attributes, sizeof(attributes)); - - NTSTATUS status = LsaOpenPolicy(0, &attributes, POLICY_ALL_ACCESS, policy); - if (status) { - print_message(stderr, NSSM_MESSAGE_LSAOPENPOLICY_FAILED, error_string(LsaNtStatusToWinError(status))); - return 1; - } - - return 0; -} - -/* Look up SID for an account. */ -int username_sid(const TCHAR *username, SID **sid, LSA_HANDLE *policy) { - LSA_HANDLE handle; - if (! policy) { - policy = &handle; - if (open_lsa_policy(policy)) return 1; - } - - /* - LsaLookupNames() can't look up .\username but can look up - %COMPUTERNAME%\username. ChangeServiceConfig() writes .\username to the - registry when %COMPUTERNAME%\username is a passed as a parameter. We - need to preserve .\username when calling ChangeServiceConfig() without - changing the username, but expand to %COMPUTERNAME%\username when calling - LsaLookupNames(). - */ - TCHAR *expanded; - unsigned long expandedlen; - if (_tcsnicmp(_T(".\\"), username, 2)) { - expandedlen = (unsigned long) (_tcslen(username) + 1) * sizeof(TCHAR); - expanded = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, expandedlen); - if (! expanded) { - print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("expanded"), _T("username_sid")); - if (policy == &handle) LsaClose(handle); - return 2; - } - memmove(expanded, username, expandedlen); - } - else { - TCHAR computername[MAX_COMPUTERNAME_LENGTH + 1]; - expandedlen = _countof(computername); - GetComputerName(computername, &expandedlen); - expandedlen += (unsigned long) _tcslen(username); - - expanded = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, expandedlen * sizeof(TCHAR)); - if (! expanded) { - print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("expanded"), _T("username_sid")); - if (policy == &handle) LsaClose(handle); - return 2; - } - _sntprintf_s(expanded, expandedlen, _TRUNCATE, _T("%s\\%s"), computername, username + 2); - } - - LSA_UNICODE_STRING lsa_username; -#ifdef UNICODE - lsa_username.Buffer = (wchar_t *) expanded; - lsa_username.Length = (unsigned short) _tcslen(expanded) * sizeof(TCHAR); - lsa_username.MaximumLength = lsa_username.Length + sizeof(TCHAR); -#else - size_t buflen; - mbstowcs_s(&buflen, NULL, 0, expanded, _TRUNCATE); - lsa_username.MaximumLength = (unsigned short) buflen * sizeof(wchar_t); - lsa_username.Length = lsa_username.MaximumLength - sizeof(wchar_t); - lsa_username.Buffer = (wchar_t *) HeapAlloc(GetProcessHeap(), 0, lsa_username.MaximumLength); - if (lsa_username.Buffer) mbstowcs_s(&buflen, lsa_username.Buffer, lsa_username.MaximumLength, expanded, _TRUNCATE); - else { - if (policy == &handle) LsaClose(handle); - HeapFree(GetProcessHeap(), 0, expanded); - print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("LSA_UNICODE_STRING"), _T("username_sid()")); - return 4; - } -#endif - - LSA_REFERENCED_DOMAIN_LIST *translated_domains; - LSA_TRANSLATED_SID *translated_sid; - NTSTATUS status = LsaLookupNames(*policy, 1, &lsa_username, &translated_domains, &translated_sid); -#ifndef UNICODE - HeapFree(GetProcessHeap(), 0, lsa_username.Buffer); -#endif - HeapFree(GetProcessHeap(), 0, expanded); - if (policy == &handle) LsaClose(handle); - if (status) { - LsaFreeMemory(translated_domains); - LsaFreeMemory(translated_sid); - print_message(stderr, NSSM_MESSAGE_LSALOOKUPNAMES_FAILED, username, error_string(LsaNtStatusToWinError(status))); - return 5; - } - - if (translated_sid->Use != SidTypeUser && translated_sid->Use != SidTypeWellKnownGroup) { - LsaFreeMemory(translated_domains); - LsaFreeMemory(translated_sid); - print_message(stderr, NSSM_GUI_INVALID_USERNAME, username); - return 6; - } - - LSA_TRUST_INFORMATION *trust = &translated_domains->Domains[translated_sid->DomainIndex]; - if (! trust || ! IsValidSid(trust->Sid)) { - LsaFreeMemory(translated_domains); - LsaFreeMemory(translated_sid); - print_message(stderr, NSSM_GUI_INVALID_USERNAME, username); - return 7; - } - - /* GetSidSubAuthority*() return pointers! */ - unsigned char *n = GetSidSubAuthorityCount(trust->Sid); - - /* Convert translated SID to SID. */ - *sid = (SID *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, GetSidLengthRequired(*n + 1)); - if (! *sid) { - LsaFreeMemory(translated_domains); - LsaFreeMemory(translated_sid); - print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SID"), _T("username_sid")); - return 8; - } - - unsigned long error; - if (! InitializeSid(*sid, GetSidIdentifierAuthority(trust->Sid), *n + 1)) { - error = GetLastError(); - HeapFree(GetProcessHeap(), 0, *sid); - LsaFreeMemory(translated_domains); - LsaFreeMemory(translated_sid); - print_message(stderr, NSSM_MESSAGE_INITIALIZESID_FAILED, username, error_string(error)); - return 9; - } - - for (unsigned char i = 0; i <= *n; i++) { - unsigned long *sub = GetSidSubAuthority(*sid, i); - if (i < *n) *sub = *GetSidSubAuthority(trust->Sid, i); - else *sub = translated_sid->RelativeId; - } - - int ret = 0; - if (translated_sid->Use == SidTypeWellKnownGroup && ! well_known_sid(*sid)) { - print_message(stderr, NSSM_GUI_INVALID_USERNAME, username); - ret = 10; - } - - LsaFreeMemory(translated_domains); - LsaFreeMemory(translated_sid); - - return ret; -} - -int username_sid(const TCHAR *username, SID **sid) { - return username_sid(username, sid, 0); -} - -int canonicalise_username(const TCHAR *username, TCHAR **canon) { - LSA_HANDLE policy; - if (open_lsa_policy(&policy)) return 1; - - SID *sid; - if (username_sid(username, &sid, &policy)) return 2; - PSID sids = { sid }; - - LSA_REFERENCED_DOMAIN_LIST *translated_domains; - LSA_TRANSLATED_NAME *translated_name; - NTSTATUS status = LsaLookupSids(policy, 1, &sids, &translated_domains, &translated_name); - if (status) { - LsaFreeMemory(translated_domains); - LsaFreeMemory(translated_name); - print_message(stderr, NSSM_MESSAGE_LSALOOKUPSIDS_FAILED, error_string(LsaNtStatusToWinError(status))); - return 3; - } - - LSA_TRUST_INFORMATION *trust = &translated_domains->Domains[translated_name->DomainIndex]; - LSA_UNICODE_STRING lsa_canon; - lsa_canon.Length = translated_name->Name.Length + trust->Name.Length + sizeof(wchar_t); - lsa_canon.MaximumLength = lsa_canon.Length + sizeof(wchar_t); - lsa_canon.Buffer = (wchar_t *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lsa_canon.MaximumLength); - if (! lsa_canon.Buffer) { - LsaFreeMemory(translated_domains); - LsaFreeMemory(translated_name); - print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("lsa_canon"), _T("username_sid")); - return 9; - } - - /* Buffer is wchar_t but Length is in bytes. */ - memmove((char *) lsa_canon.Buffer, trust->Name.Buffer, trust->Name.Length); - memmove((char *) lsa_canon.Buffer + trust->Name.Length, L"\\", sizeof(wchar_t)); - memmove((char *) lsa_canon.Buffer + trust->Name.Length + sizeof(wchar_t), translated_name->Name.Buffer, translated_name->Name.Length); - -#ifdef UNICODE - *canon = lsa_canon.Buffer; -#else - size_t buflen; - wcstombs_s(&buflen, NULL, 0, lsa_canon.Buffer, _TRUNCATE); - *canon = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, buflen); - if (! *canon) { - LsaFreeMemory(translated_domains); - LsaFreeMemory(translated_name); - print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canon"), _T("username_sid")); - return 10; - } - wcstombs_s(&buflen, *canon, buflen, lsa_canon.Buffer, _TRUNCATE); - HeapFree(GetProcessHeap(), 0, lsa_canon.Buffer); -#endif - - LsaFreeMemory(translated_domains); - LsaFreeMemory(translated_name); - - return 0; -} - -/* Do two usernames map to the same SID? */ -int username_equiv(const TCHAR *a, const TCHAR *b) { - SID *sid_a, *sid_b; - if (username_sid(a, &sid_a)) return 0; - - if (username_sid(b, &sid_b)) { - FreeSid(sid_a); - return 0; - } - - int ret = 0; - if (EqualSid(sid_a, sid_b)) ret = 1; - - FreeSid(sid_a); - FreeSid(sid_b); - - return ret; -} - -/* Does the username represent the LocalSystem account? */ -int is_localsystem(const TCHAR *username) { - if (str_equiv(username, NSSM_LOCALSYSTEM_ACCOUNT)) return 1; - if (! imports.IsWellKnownSid) return 0; - - SID *sid; - if (username_sid(username, &sid)) return 0; - - int ret = 0; - if (imports.IsWellKnownSid(sid, WinLocalSystemSid)) ret = 1; - - FreeSid(sid); - - return ret; -} - -/* - Get well-known alias for LocalSystem and friends. - Returns a pointer to a static string. DO NOT try to free it. -*/ -const TCHAR *well_known_sid(SID *sid) { - if (! imports.IsWellKnownSid) return 0; - if (imports.IsWellKnownSid(sid, WinLocalSystemSid)) return NSSM_LOCALSYSTEM_ACCOUNT; - if (imports.IsWellKnownSid(sid, WinLocalServiceSid)) return NSSM_LOCALSERVICE_ACCOUNT; - if (imports.IsWellKnownSid(sid, WinNetworkServiceSid)) return NSSM_NETWORKSERVICE_ACCOUNT; - return 0; -} - -const TCHAR *well_known_username(const TCHAR *username) { - if (! username) return NSSM_LOCALSYSTEM_ACCOUNT; - if (str_equiv(username, NSSM_LOCALSYSTEM_ACCOUNT)) return NSSM_LOCALSYSTEM_ACCOUNT; - SID *sid; - if (username_sid(username, &sid)) return 0; - - const TCHAR *well_known = well_known_sid(sid); - FreeSid(sid); - - return well_known; -} - -int grant_logon_as_service(const TCHAR *username) { - if (! username) return 0; - - /* Open Policy object. */ - LSA_OBJECT_ATTRIBUTES attributes; - ZeroMemory(&attributes, sizeof(attributes)); - - LSA_HANDLE policy; - NTSTATUS status; - - if (open_lsa_policy(&policy)) return 1; - - /* Look up SID for the account. */ - SID *sid; - if (username_sid(username, &sid, &policy)) { - LsaClose(policy); - return 2; - } - - /* - Shouldn't happen because it should have been checked before callling this function. - */ - if (well_known_sid(sid)) { - LsaClose(policy); - return 3; - } - - /* Check if the SID has the "Log on as a service" right. */ - LSA_UNICODE_STRING lsa_right; - lsa_right.Buffer = NSSM_LOGON_AS_SERVICE_RIGHT; - lsa_right.Length = (unsigned short) wcslen(lsa_right.Buffer) * sizeof(wchar_t); - lsa_right.MaximumLength = lsa_right.Length + sizeof(wchar_t); - - LSA_UNICODE_STRING *rights; - unsigned long count = ~0; - status = LsaEnumerateAccountRights(policy, sid, &rights, &count); - if (status) { - /* - If the account has no rights set LsaEnumerateAccountRights() will return - STATUS_OBJECT_NAME_NOT_FOUND and set count to 0. - */ - unsigned long error = LsaNtStatusToWinError(status); - if (error != ERROR_FILE_NOT_FOUND) { - FreeSid(sid); - LsaClose(policy); - print_message(stderr, NSSM_MESSAGE_LSAENUMERATEACCOUNTRIGHTS_FAILED, username, error_string(error)); - return 4; - } - } - - for (unsigned long i = 0; i < count; i++) { - if (rights[i].Length != lsa_right.Length) continue; - if (_wcsnicmp(rights[i].Buffer, lsa_right.Buffer, lsa_right.MaximumLength)) continue; - /* The SID has the right. */ - FreeSid(sid); - LsaFreeMemory(rights); - LsaClose(policy); - return 0; - } - LsaFreeMemory(rights); - - /* Add the right. */ - status = LsaAddAccountRights(policy, sid, &lsa_right, 1); - FreeSid(sid); - LsaClose(policy); - if (status) { - print_message(stderr, NSSM_MESSAGE_LSAADDACCOUNTRIGHTS_FAILED, error_string(LsaNtStatusToWinError(status))); - return 5; - } - - print_message(stdout, NSSM_MESSAGE_GRANTED_LOGON_AS_SERVICE, username); - return 0; -} +#include "nssm.h" + +#include + +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; +} diff --git a/account.h b/account.h index e6b40f0..232e1f9 100644 --- a/account.h +++ b/account.h @@ -1,24 +1,24 @@ -#ifndef ACCOUNT_H -#define ACCOUNT_H - -#include - -/* Not really an account. The canonical name is NT Authority\System. */ -#define NSSM_LOCALSYSTEM_ACCOUNT _T("LocalSystem") -/* Other well-known accounts which can start a service without a password. */ -#define NSSM_LOCALSERVICE_ACCOUNT _T("NT Authority\\LocalService") -#define NSSM_NETWORKSERVICE_ACCOUNT _T("NT Authority\\NetworkService") -/* This is explicitly a wide string. */ -#define NSSM_LOGON_AS_SERVICE_RIGHT L"SeServiceLogonRight" - -int open_lsa_policy(LSA_HANDLE *); -int username_sid(const TCHAR *, SID **, LSA_HANDLE *); -int username_sid(const TCHAR *, SID **); -int username_equiv(const TCHAR *, const TCHAR *); -int canonicalise_username(const TCHAR *, TCHAR **); -int is_localsystem(const TCHAR *); -const TCHAR *well_known_sid(SID *); -const TCHAR *well_known_username(const TCHAR *); -int grant_logon_as_service(const TCHAR *); - -#endif +#ifndef ACCOUNT_H +#define ACCOUNT_H + +#include + +/* 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 diff --git a/console.cpp b/console.cpp index 66c99ea..9a97729 100644 --- a/console.cpp +++ b/console.cpp @@ -1,175 +1,175 @@ -#include "nssm.h" - -/* See if we were launched from a console window. */ -void check_console() { - /* If we're running in a service context there will be no console window. */ - HWND console = GetConsoleWindow(); - if (! console) return; - - unsigned long pid; - if (! GetWindowThreadProcessId(console, &pid)) return; - - /* - If the process associated with the console window handle is the same as - this process, we were not launched from an existing console. The user - probably double-clicked our executable. - */ - if (GetCurrentProcessId() != pid) return; - - /* We close our new console so that subsequent messages appear in a popup. */ - FreeConsole(); -} - -/* Helpers for drawing the banner. */ -static inline void block(unsigned int a, short x, short y, unsigned long n) { - HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE); - TCHAR s = _T(' '); - - unsigned long out; - COORD c = { x, y }; - FillConsoleOutputAttribute(h, a, n, c, &out); - FillConsoleOutputCharacter(h, s, n, c, &out); -} - -static inline void R(short x, short y, unsigned long n) { - block(BACKGROUND_RED | BACKGROUND_INTENSITY, x, y, n); -} - -static inline void r(short x, short y, unsigned long n) { - block(BACKGROUND_RED, x, y, n); -} - -static inline void b(short x, short y, unsigned long n) { - block(0, x, y, n); -} - -void alloc_console(nssm_service_t *service) { - if (service->no_console) return; - - AllocConsole(); - - /* Disable accidental closure. */ - HWND window = GetConsoleWindow(); - HMENU menu = GetSystemMenu(window, false); - EnableMenuItem(menu, SC_CLOSE, MF_GRAYED); - - /* Set a title like "[NSSM] Jenkins" */ - TCHAR displayname[SERVICE_NAME_LENGTH]; - unsigned long len = _countof(displayname); - SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT); - if (services) { - if (! GetServiceDisplayName(services, service->name, displayname, &len)) ZeroMemory(displayname, sizeof(displayname)); - CloseServiceHandle(services); - } - if (! displayname[0]) _sntprintf_s(displayname, _countof(displayname), _TRUNCATE, _T("%s"), service->name); - - TCHAR title[65535]; - _sntprintf_s(title, _countof(title), _TRUNCATE, _T("[%s] %s"), NSSM, displayname); - SetConsoleTitle(title); - - /* Draw the NSSM logo on the console window. */ - short y = 0; - - b(0, y, 80); - y++; - - b(0, y, 80); - y++; - - b(0, y, 80); - y++; - - b(0, y, 80); - y++; - - b(0, y, 80); - r(18, y, 5); r(28, y, 4); r(41, y, 4); r(68, y, 1); - R(6, y, 5); R(19, y, 4); R(29, y, 1); R(32, y, 3); R(42, y, 1); R(45, y, 3); R(52, y, 5); R(69, y, 4); - y++; - - b(0, y, 80); - r(8, y, 4); r(20, y, 1); r(28, y, 1); r(33, y, 3); r(41, y, 1); r(46, y, 3); r (57, y, 1); - R(9, y, 2); R(21, y, 1); R(27, y, 1); R(34, y, 1); R(40, y, 1); R(47, y, 1); R(54, y, 3); R(68, y, 3); - y++; - - b(0, y, 80); - r(12, y, 1); r(20, y, 1); r(26, y, 1); r(34, y, 2); r(39, y, 1); r(47, y, 2); r(67, y, 2); - R(9, y, 3); R(21, y, 1); R(27, y, 1); R(40, y, 1); R(54, y, 1); R(56, y, 2); R(67, y, 1); R(69, y, 2); - y++; - - b(0, y, 80); - r(9, y, 1); r(20, y, 1); r(26, y, 1); r (35, y, 1); r(39, y, 1); r(48, y, 1); r(58, y, 1); - R(10, y, 3); R(21, y, 1); R(27, y, 1); R(40, y, 1); R(54, y, 1); R(56, y, 2); R(67, y, 1); R(69, y, 2); - y++; - - b(0, y, 80); - r(9, y, 1); r(56, y, 1); r(66, y, 2); - R(11, y, 3); R(21, y, 1); R(26, y, 2); R(39, y, 2); R(54, y, 1); R(57, y, 2); R(69, y, 2); - y++; - - b(0, y, 80); - r(9, y, 1); r(26, y, 1); r(39, y, 1); r(59, y, 1); - R(12, y, 3); R(21, y, 1); R(27, y, 2); R(40, y, 2); R(54, y, 1); R(57, y, 2); R(66, y, 1); R(69, y, 2); - y++; - - b(0, y, 80); - r(9, y, 1); r(12, y, 4); r(30, y, 1); r(43, y, 1); r(57, y, 1); r(65, y, 2); - R(13, y, 2); R(21, y, 1); R(27, y, 3); R(40, y, 3); R(54, y, 1); R(58, y, 2); R(69, y, 2); - y++; - - b(0, y, 80); - r(9, y, 1); r(13, y, 4); r(27, y, 7); r(40, y, 7); - R(14, y, 2); R(21, y, 1); R(28, y, 5); R(41, y, 5); R(54, y, 1); R(58, y, 2); R(65, y, 1); R(69, y, 2); - y++; - - b(0, y, 80); - r(9, y, 1); r(60, y, 1); r(65, y, 1); - R(14, y, 3); R(21, y, 1); R(29, y, 6); R(42, y, 6); R(54, y, 1); R(58, y, 2); R(69, y, 2); - y++; - - b(0, y, 80); - r(9, y, 1); r(31, y, 1); r(44, y, 1); r(58, y, 1); r(64, y, 1); - R(15, y, 3); R(21, y, 1); R(32, y, 4); R(45, y, 4); R(54, y, 1); R(59, y, 2); R(69, y, 2); - y++; - - b(0, y, 80); - r(9, y, 1); r(33, y, 1); r(46, y, 1); r(61, y, 1); r(64, y, 1); - R(16, y, 3); R(21, y, 1); R(34, y, 2); R(47, y, 2); R(54, y, 1); R(59, y, 2); R(69, y, 2); - y++; - - b(0, y, 80); - r(9, y, 1); r(16, y, 4); r(36, y, 1); r(49, y, 1); r(59, y, 1); r(63, y, 1); - R(17, y, 2); R(21, y, 1); R(34, y, 2); R(47, y, 2); R(54, y, 1); R(60, y, 2); R(69, y, 2); - y++; - - b(0, y, 80); - r(9, y, 1); r(17, y, 4); r(26, y, 1); r(36, y, 1); r(39, y, 1); r(49, y, 1); - R(18, y, 2); R(21, y, 1); R(35, y, 1); R(48, y, 1); R(54, y, 1); R(60, y, 2); R(63, y, 1); R(69, y, 2); - y++; - - b(0, y, 80); - r(26, y, 2); r(39, y, 2); r(63, y, 1); - R(9, y, 1); R(18, y, 4); R(35, y, 1); R(48, y, 1); R(54, y, 1); R(60, y, 3); R(69, y, 2); - y++; - - b(0, y, 80); - r(34, y, 1); r(47, y, 1); r(60, y, 1); - R(9, y, 1); R(19, y, 3); R(26, y, 2); R(35, y, 1); R(39, y, 2); R(48, y, 1); R(54, y, 1); R(61, y, 2); R(69, y, 2); - y++; - - b(0, y, 80); - r(8, y, 1); r(35, y, 1); r(48, y, 1); r(62, y, 1); r(71, y, 1); - R(9, y, 1); R(20, y, 2); R(26, y, 3); R(34, y, 1); R(39, y, 3); R(47, y, 1); R(54, y, 1); R(61, y, 1); R(69, y, 2); - y++; - - b(0, y, 80); - r(11, y, 1); r(26, y, 1); r(28, y, 5); r(39, y, 1); r(41, y, 5); r(51, y, 7); r(61, y, 1); r(66, y, 8); - R(7, y, 4); R(21, y, 1); R(29, y, 1); R(33, y, 1); R(42, y, 1); R(46, y, 1); R(52, y, 5); R(67, y, 7); - y++; - - b(0, y, 80); - y++; - - b(0, y, 80); - y++; -} +#include "nssm.h" + +/* See if we were launched from a console window. */ +void check_console() { + /* If we're running in a service context there will be no console window. */ + HWND console = GetConsoleWindow(); + if (! console) return; + + unsigned long pid; + if (! GetWindowThreadProcessId(console, &pid)) return; + + /* + If the process associated with the console window handle is the same as + this process, we were not launched from an existing console. The user + probably double-clicked our executable. + */ + if (GetCurrentProcessId() != pid) return; + + /* We close our new console so that subsequent messages appear in a popup. */ + FreeConsole(); +} + +/* Helpers for drawing the banner. */ +static inline void block(unsigned int a, short x, short y, unsigned long n) { + HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE); + TCHAR s = _T(' '); + + unsigned long out; + COORD c = { x, y }; + FillConsoleOutputAttribute(h, a, n, c, &out); + FillConsoleOutputCharacter(h, s, n, c, &out); +} + +static inline void R(short x, short y, unsigned long n) { + block(BACKGROUND_RED | BACKGROUND_INTENSITY, x, y, n); +} + +static inline void r(short x, short y, unsigned long n) { + block(BACKGROUND_RED, x, y, n); +} + +static inline void b(short x, short y, unsigned long n) { + block(0, x, y, n); +} + +void alloc_console(nssm_service_t *service) { + if (service->no_console) return; + + AllocConsole(); + + /* Disable accidental closure. */ + HWND window = GetConsoleWindow(); + HMENU menu = GetSystemMenu(window, false); + EnableMenuItem(menu, SC_CLOSE, MF_GRAYED); + + /* Set a title like "[NSSM] Jenkins" */ + TCHAR displayname[SERVICE_NAME_LENGTH]; + unsigned long len = _countof(displayname); + SC_HANDLE services = open_service_manager(SC_MANAGER_CONNECT); + if (services) { + if (! GetServiceDisplayName(services, service->name, displayname, &len)) ZeroMemory(displayname, sizeof(displayname)); + CloseServiceHandle(services); + } + if (! displayname[0]) _sntprintf_s(displayname, _countof(displayname), _TRUNCATE, _T("%s"), service->name); + + TCHAR title[65535]; + _sntprintf_s(title, _countof(title), _TRUNCATE, _T("[%s] %s"), NSSM, displayname); + SetConsoleTitle(title); + + /* Draw the NSSM logo on the console window. */ + short y = 0; + + b(0, y, 80); + y++; + + b(0, y, 80); + y++; + + b(0, y, 80); + y++; + + b(0, y, 80); + y++; + + b(0, y, 80); + r(18, y, 5); r(28, y, 4); r(41, y, 4); r(68, y, 1); + R(6, y, 5); R(19, y, 4); R(29, y, 1); R(32, y, 3); R(42, y, 1); R(45, y, 3); R(52, y, 5); R(69, y, 4); + y++; + + b(0, y, 80); + r(8, y, 4); r(20, y, 1); r(28, y, 1); r(33, y, 3); r(41, y, 1); r(46, y, 3); r (57, y, 1); + R(9, y, 2); R(21, y, 1); R(27, y, 1); R(34, y, 1); R(40, y, 1); R(47, y, 1); R(54, y, 3); R(68, y, 3); + y++; + + b(0, y, 80); + r(12, y, 1); r(20, y, 1); r(26, y, 1); r(34, y, 2); r(39, y, 1); r(47, y, 2); r(67, y, 2); + R(9, y, 3); R(21, y, 1); R(27, y, 1); R(40, y, 1); R(54, y, 1); R(56, y, 2); R(67, y, 1); R(69, y, 2); + y++; + + b(0, y, 80); + r(9, y, 1); r(20, y, 1); r(26, y, 1); r (35, y, 1); r(39, y, 1); r(48, y, 1); r(58, y, 1); + R(10, y, 3); R(21, y, 1); R(27, y, 1); R(40, y, 1); R(54, y, 1); R(56, y, 2); R(67, y, 1); R(69, y, 2); + y++; + + b(0, y, 80); + r(9, y, 1); r(56, y, 1); r(66, y, 2); + R(11, y, 3); R(21, y, 1); R(26, y, 2); R(39, y, 2); R(54, y, 1); R(57, y, 2); R(69, y, 2); + y++; + + b(0, y, 80); + r(9, y, 1); r(26, y, 1); r(39, y, 1); r(59, y, 1); + R(12, y, 3); R(21, y, 1); R(27, y, 2); R(40, y, 2); R(54, y, 1); R(57, y, 2); R(66, y, 1); R(69, y, 2); + y++; + + b(0, y, 80); + r(9, y, 1); r(12, y, 4); r(30, y, 1); r(43, y, 1); r(57, y, 1); r(65, y, 2); + R(13, y, 2); R(21, y, 1); R(27, y, 3); R(40, y, 3); R(54, y, 1); R(58, y, 2); R(69, y, 2); + y++; + + b(0, y, 80); + r(9, y, 1); r(13, y, 4); r(27, y, 7); r(40, y, 7); + R(14, y, 2); R(21, y, 1); R(28, y, 5); R(41, y, 5); R(54, y, 1); R(58, y, 2); R(65, y, 1); R(69, y, 2); + y++; + + b(0, y, 80); + r(9, y, 1); r(60, y, 1); r(65, y, 1); + R(14, y, 3); R(21, y, 1); R(29, y, 6); R(42, y, 6); R(54, y, 1); R(58, y, 2); R(69, y, 2); + y++; + + b(0, y, 80); + r(9, y, 1); r(31, y, 1); r(44, y, 1); r(58, y, 1); r(64, y, 1); + R(15, y, 3); R(21, y, 1); R(32, y, 4); R(45, y, 4); R(54, y, 1); R(59, y, 2); R(69, y, 2); + y++; + + b(0, y, 80); + r(9, y, 1); r(33, y, 1); r(46, y, 1); r(61, y, 1); r(64, y, 1); + R(16, y, 3); R(21, y, 1); R(34, y, 2); R(47, y, 2); R(54, y, 1); R(59, y, 2); R(69, y, 2); + y++; + + b(0, y, 80); + r(9, y, 1); r(16, y, 4); r(36, y, 1); r(49, y, 1); r(59, y, 1); r(63, y, 1); + R(17, y, 2); R(21, y, 1); R(34, y, 2); R(47, y, 2); R(54, y, 1); R(60, y, 2); R(69, y, 2); + y++; + + b(0, y, 80); + r(9, y, 1); r(17, y, 4); r(26, y, 1); r(36, y, 1); r(39, y, 1); r(49, y, 1); + R(18, y, 2); R(21, y, 1); R(35, y, 1); R(48, y, 1); R(54, y, 1); R(60, y, 2); R(63, y, 1); R(69, y, 2); + y++; + + b(0, y, 80); + r(26, y, 2); r(39, y, 2); r(63, y, 1); + R(9, y, 1); R(18, y, 4); R(35, y, 1); R(48, y, 1); R(54, y, 1); R(60, y, 3); R(69, y, 2); + y++; + + b(0, y, 80); + r(34, y, 1); r(47, y, 1); r(60, y, 1); + R(9, y, 1); R(19, y, 3); R(26, y, 2); R(35, y, 1); R(39, y, 2); R(48, y, 1); R(54, y, 1); R(61, y, 2); R(69, y, 2); + y++; + + b(0, y, 80); + r(8, y, 1); r(35, y, 1); r(48, y, 1); r(62, y, 1); r(71, y, 1); + R(9, y, 1); R(20, y, 2); R(26, y, 3); R(34, y, 1); R(39, y, 3); R(47, y, 1); R(54, y, 1); R(61, y, 1); R(69, y, 2); + y++; + + b(0, y, 80); + r(11, y, 1); r(26, y, 1); r(28, y, 5); r(39, y, 1); r(41, y, 5); r(51, y, 7); r(61, y, 1); r(66, y, 8); + R(7, y, 4); R(21, y, 1); R(29, y, 1); R(33, y, 1); R(42, y, 1); R(46, y, 1); R(52, y, 5); R(67, y, 7); + y++; + + b(0, y, 80); + y++; + + b(0, y, 80); + y++; +} diff --git a/console.h b/console.h index 8aa8966..cbccd3c 100644 --- a/console.h +++ b/console.h @@ -1,7 +1,7 @@ -#ifndef CONSOLE_H -#define CONSOLE_H - -void check_console(); -void alloc_console(nssm_service_t *); - -#endif +#ifndef CONSOLE_H +#define CONSOLE_H + +void check_console(); +void alloc_console(nssm_service_t *); + +#endif diff --git a/env.cpp b/env.cpp index a1ca900..4427d91 100644 --- a/env.cpp +++ b/env.cpp @@ -1,181 +1,181 @@ -#include "nssm.h" - -/* Copy an environment block. */ -TCHAR *copy_environment_block(TCHAR *env) { - unsigned long len; - - if (! env) return 0; - for (len = 0; env[len]; len++) while (env[len]) len++; - if (! len++) return 0; - - TCHAR *newenv = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR)); - if (! newenv) { - log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("environment"), _T("copy_environment_block()"), 0); - return 0; - } - - memmove(newenv, env, len * sizeof(TCHAR)); - return newenv; -} - -/* - The environment block starts with variables of the form - =C:=C:\Windows\System32 which we ignore. -*/ -TCHAR *useful_environment(TCHAR *rawenv) { - TCHAR *env = rawenv; - - if (env) { - while (*env == _T('=')) { - for ( ; *env; env++); - env++; - } - } - - return env; -} - -/* Expand an environment variable. Must call HeapFree() on the result. */ -TCHAR *expand_environment_string(TCHAR *string) { - unsigned long len; - - len = ExpandEnvironmentStrings(string, 0, 0); - if (! len) { - log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_EXPANDENVIRONMENTSTRINGS_FAILED, string, error_string(GetLastError()), 0); - return 0; - } - - TCHAR *ret = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR)); - if (! ret) { - log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("ExpandEnvironmentStrings()"), _T("expand_environment_string"), 0); - return 0; - } - - if (! ExpandEnvironmentStrings(string, ret, len)) { - log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_EXPANDENVIRONMENTSTRINGS_FAILED, string, error_string(GetLastError()), 0); - HeapFree(GetProcessHeap(), 0, ret); - return 0; - } - - return ret; -} - -/* - Set all the environment variables from an environment block in the current - environment or remove all the variables in the block from the current - environment. -*/ -static int set_environment_block(TCHAR *env, bool set) { - int ret = 0; - - TCHAR *s, *t; - for (s = env; *s; s++) { - for (t = s; *t && *t != _T('='); t++); - if (*t == _T('=')) { - *t = _T('\0'); - if (set) { - TCHAR *expanded = expand_environment_string(++t); - if (expanded) { - if (! SetEnvironmentVariable(s, expanded)) ret++; - HeapFree(GetProcessHeap(), 0, expanded); - } - else { - if (! SetEnvironmentVariable(s, t)) ret++; - } - } - else { - if (! SetEnvironmentVariable(s, NULL)) ret++; - } - for (t++; *t; t++); - } - s = t; - } - - return ret; -} - -int set_environment_block(TCHAR *env) { - return set_environment_block(env, true); -} - -static int unset_environment_block(TCHAR *env) { - return set_environment_block(env, false); -} - -/* Remove all variables from the process environment. */ -int clear_environment() { - TCHAR *rawenv = GetEnvironmentStrings(); - TCHAR *env = useful_environment(rawenv); - - int ret = unset_environment_block(env); - - if (rawenv) FreeEnvironmentStrings(rawenv); - - return ret; -} - -/* Set the current environment to exactly duplicate an environment block. */ -int duplicate_environment(TCHAR *rawenv) { - int ret = clear_environment(); - TCHAR *env = useful_environment(rawenv); - ret += set_environment_block(env); - return ret; -} - -/* - Verify an environment block. - Returns: 1 if environment is invalid. - 0 if environment is OK. - -1 on error. -*/ -int test_environment(TCHAR *env) { - TCHAR *path = (TCHAR *) nssm_imagepath(); - STARTUPINFO si; - ZeroMemory(&si, sizeof(si)); - si.cb = sizeof(si); - PROCESS_INFORMATION pi; - ZeroMemory(&pi, sizeof(pi)); - unsigned long flags = CREATE_SUSPENDED; -#ifdef UNICODE - flags |= CREATE_UNICODE_ENVIRONMENT; -#endif - - /* - Try to relaunch ourselves but with the candidate environment set. - Assuming no solar flare activity, the only reason this would fail is if - the environment were invalid. - */ - if (CreateProcess(0, path, 0, 0, 0, flags, env, 0, &si, &pi)) { - TerminateProcess(pi.hProcess, 0); - } - else { - unsigned long error = GetLastError(); - if (error == ERROR_INVALID_PARAMETER) return 1; - else return -1; - } - - return 0; -} - -/* - Duplicate an environment block returned by GetEnvironmentStrings(). - Since such a block is by definition readonly, and duplicate_environment() - modifies its inputs, this function takes a copy of the input and operates - on that. -*/ -void duplicate_environment_strings(TCHAR *env) { - TCHAR *newenv = copy_environment_block(env); - if (! newenv) return; - - duplicate_environment(newenv); - HeapFree(GetProcessHeap(), 0, newenv); -} - -/* Safely get a copy of the current environment. */ -TCHAR *copy_environment() { - TCHAR *rawenv = GetEnvironmentStrings(); - if (! rawenv) return NULL; - TCHAR *env = copy_environment_block(rawenv); - FreeEnvironmentStrings(rawenv); - return env; -} +#include "nssm.h" + +/* Copy an environment block. */ +TCHAR *copy_environment_block(TCHAR *env) { + unsigned long len; + + if (! env) return 0; + for (len = 0; env[len]; len++) while (env[len]) len++; + if (! len++) return 0; + + TCHAR *newenv = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR)); + if (! newenv) { + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("environment"), _T("copy_environment_block()"), 0); + return 0; + } + + memmove(newenv, env, len * sizeof(TCHAR)); + return newenv; +} + +/* + The environment block starts with variables of the form + =C:=C:\Windows\System32 which we ignore. +*/ +TCHAR *useful_environment(TCHAR *rawenv) { + TCHAR *env = rawenv; + + if (env) { + while (*env == _T('=')) { + for ( ; *env; env++); + env++; + } + } + + return env; +} + +/* Expand an environment variable. Must call HeapFree() on the result. */ +TCHAR *expand_environment_string(TCHAR *string) { + unsigned long len; + + len = ExpandEnvironmentStrings(string, 0, 0); + if (! len) { + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_EXPANDENVIRONMENTSTRINGS_FAILED, string, error_string(GetLastError()), 0); + return 0; + } + + TCHAR *ret = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR)); + if (! ret) { + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("ExpandEnvironmentStrings()"), _T("expand_environment_string"), 0); + return 0; + } + + if (! ExpandEnvironmentStrings(string, ret, len)) { + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_EXPANDENVIRONMENTSTRINGS_FAILED, string, error_string(GetLastError()), 0); + HeapFree(GetProcessHeap(), 0, ret); + return 0; + } + + return ret; +} + +/* + Set all the environment variables from an environment block in the current + environment or remove all the variables in the block from the current + environment. +*/ +static int set_environment_block(TCHAR *env, bool set) { + int ret = 0; + + TCHAR *s, *t; + for (s = env; *s; s++) { + for (t = s; *t && *t != _T('='); t++); + if (*t == _T('=')) { + *t = _T('\0'); + if (set) { + TCHAR *expanded = expand_environment_string(++t); + if (expanded) { + if (! SetEnvironmentVariable(s, expanded)) ret++; + HeapFree(GetProcessHeap(), 0, expanded); + } + else { + if (! SetEnvironmentVariable(s, t)) ret++; + } + } + else { + if (! SetEnvironmentVariable(s, NULL)) ret++; + } + for (t++; *t; t++); + } + s = t; + } + + return ret; +} + +int set_environment_block(TCHAR *env) { + return set_environment_block(env, true); +} + +static int unset_environment_block(TCHAR *env) { + return set_environment_block(env, false); +} + +/* Remove all variables from the process environment. */ +int clear_environment() { + TCHAR *rawenv = GetEnvironmentStrings(); + TCHAR *env = useful_environment(rawenv); + + int ret = unset_environment_block(env); + + if (rawenv) FreeEnvironmentStrings(rawenv); + + return ret; +} + +/* Set the current environment to exactly duplicate an environment block. */ +int duplicate_environment(TCHAR *rawenv) { + int ret = clear_environment(); + TCHAR *env = useful_environment(rawenv); + ret += set_environment_block(env); + return ret; +} + +/* + Verify an environment block. + Returns: 1 if environment is invalid. + 0 if environment is OK. + -1 on error. +*/ +int test_environment(TCHAR *env) { + TCHAR *path = (TCHAR *) nssm_imagepath(); + STARTUPINFO si; + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + PROCESS_INFORMATION pi; + ZeroMemory(&pi, sizeof(pi)); + unsigned long flags = CREATE_SUSPENDED; +#ifdef UNICODE + flags |= CREATE_UNICODE_ENVIRONMENT; +#endif + + /* + Try to relaunch ourselves but with the candidate environment set. + Assuming no solar flare activity, the only reason this would fail is if + the environment were invalid. + */ + if (CreateProcess(0, path, 0, 0, 0, flags, env, 0, &si, &pi)) { + TerminateProcess(pi.hProcess, 0); + } + else { + unsigned long error = GetLastError(); + if (error == ERROR_INVALID_PARAMETER) return 1; + else return -1; + } + + return 0; +} + +/* + Duplicate an environment block returned by GetEnvironmentStrings(). + Since such a block is by definition readonly, and duplicate_environment() + modifies its inputs, this function takes a copy of the input and operates + on that. +*/ +void duplicate_environment_strings(TCHAR *env) { + TCHAR *newenv = copy_environment_block(env); + if (! newenv) return; + + duplicate_environment(newenv); + HeapFree(GetProcessHeap(), 0, newenv); +} + +/* Safely get a copy of the current environment. */ +TCHAR *copy_environment() { + TCHAR *rawenv = GetEnvironmentStrings(); + if (! rawenv) return NULL; + TCHAR *env = copy_environment_block(rawenv); + FreeEnvironmentStrings(rawenv); + return env; +} diff --git a/env.h b/env.h index b690106..a56456d 100644 --- a/env.h +++ b/env.h @@ -1,14 +1,14 @@ -#ifndef ENV_H -#define ENV_H - -TCHAR *copy_environment_block(TCHAR *); -TCHAR *useful_environment(TCHAR *); -TCHAR *expand_environment_string(TCHAR *); -int set_environment_block(TCHAR *); -int clear_environment(); -int duplicate_environment(TCHAR *); -int test_environment(TCHAR *); -void duplicate_environment_strings(TCHAR *); -TCHAR *copy_environment(); - -#endif +#ifndef ENV_H +#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 diff --git a/hook.cpp b/hook.cpp index 780590b..01e3a50 100644 --- a/hook.cpp +++ b/hook.cpp @@ -1,402 +1,402 @@ -#include "nssm.h" - -typedef struct { - TCHAR *name; - HANDLE process_handle; - unsigned long pid; - unsigned long deadline; - FILETIME creation_time; - kill_t k; -} hook_t; - -static unsigned long WINAPI await_hook(void *arg) { - hook_t *hook = (hook_t *) arg; - if (! hook) return NSSM_HOOK_STATUS_ERROR; - - int ret = 0; - if (WaitForSingleObject(hook->process_handle, hook->deadline) == WAIT_TIMEOUT) ret = NSSM_HOOK_STATUS_TIMEOUT; - - /* Tidy up hook process tree. */ - if (hook->name) hook->k.name = hook->name; - else hook->k.name = _T("hook"); - hook->k.process_handle = hook->process_handle; - hook->k.pid = hook->pid; - hook->k.stop_method = ~0; - hook->k.kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD; - hook->k.kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD; - hook->k.kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD; - hook->k.creation_time = hook->creation_time; - GetSystemTimeAsFileTime(&hook->k.exit_time); - kill_process_tree(&hook->k, hook->pid); - - if (ret) { - CloseHandle(hook->process_handle); - if (hook->name) HeapFree(GetProcessHeap(), 0, hook->name); - HeapFree(GetProcessHeap(), 0, hook); - return ret; - } - - unsigned long exitcode; - GetExitCodeProcess(hook->process_handle, &exitcode); - CloseHandle(hook->process_handle); - - if (hook->name) HeapFree(GetProcessHeap(), 0, hook->name); - HeapFree(GetProcessHeap(), 0, hook); - - if (exitcode == NSSM_HOOK_STATUS_ABORT) return NSSM_HOOK_STATUS_ABORT; - if (exitcode) return NSSM_HOOK_STATUS_FAILED; - - return NSSM_HOOK_STATUS_SUCCESS; -} - -static void set_hook_runtime(TCHAR *v, FILETIME *start, FILETIME *now) { - if (start && now) { - ULARGE_INTEGER s; - s.LowPart = start->dwLowDateTime; - s.HighPart = start->dwHighDateTime; - if (s.QuadPart) { - ULARGE_INTEGER t; - t.LowPart = now->dwLowDateTime; - t.HighPart = now->dwHighDateTime; - if (t.QuadPart && t.QuadPart >= s.QuadPart) { - t.QuadPart -= s.QuadPart; - t.QuadPart /= 10000LL; - TCHAR number[16]; - _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%llu"), t.QuadPart); - SetEnvironmentVariable(v, number); - return; - } - } - } - SetEnvironmentVariable(v, _T("")); -} - -static void add_thread_handle(hook_thread_t *hook_threads, HANDLE thread_handle, TCHAR *name) { - if (! hook_threads) return; - - int num_threads = hook_threads->num_threads + 1; - hook_thread_data_t *data = (hook_thread_data_t *) HeapAlloc(GetProcessHeap(), 0, num_threads * sizeof(hook_thread_data_t)); - if (! data) { - log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("hook_thread_t"), _T("add_thread_handle()"), 0); - return; - } - - int i; - for (i = 0; i < hook_threads->num_threads; i++) memmove(&data[i], &hook_threads->data[i], sizeof(data[i])); - memmove(data[i].name, name, sizeof(data[i].name)); - data[i].thread_handle = thread_handle; - - if (hook_threads->data) HeapFree(GetProcessHeap(), 0, hook_threads->data); - hook_threads->data = data; - hook_threads->num_threads = num_threads; -} - -bool valid_hook_name(const TCHAR *hook_event, const TCHAR *hook_action, bool quiet) { - bool valid_event = false; - bool valid_action = false; - - /* Exit/Post */ - if (str_equiv(hook_event, NSSM_HOOK_EVENT_EXIT)) { - if (str_equiv(hook_action, NSSM_HOOK_ACTION_POST)) return true; - if (quiet) return false; - print_message(stderr, NSSM_MESSAGE_INVALID_HOOK_ACTION, hook_event); - _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_ACTION_POST); - return false; - } - - /* Power/{Change,Resume} */ - if (str_equiv(hook_event, NSSM_HOOK_EVENT_POWER)) { - if (str_equiv(hook_action, NSSM_HOOK_ACTION_CHANGE)) return true; - if (str_equiv(hook_action, NSSM_HOOK_ACTION_RESUME)) return true; - if (quiet) return false; - print_message(stderr, NSSM_MESSAGE_INVALID_HOOK_ACTION, hook_event); - _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_ACTION_CHANGE); - _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_ACTION_RESUME); - return false; - } - - /* Rotate/{Pre,Post} */ - if (str_equiv(hook_event, NSSM_HOOK_EVENT_ROTATE)) { - if (str_equiv(hook_action, NSSM_HOOK_ACTION_PRE)) return true; - if (str_equiv(hook_action, NSSM_HOOK_ACTION_POST)) return true; - if (quiet) return false; - print_message(stderr, NSSM_MESSAGE_INVALID_HOOK_ACTION, hook_event); - _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_ACTION_PRE); - _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_ACTION_POST); - return false; - } - - /* Start/{Pre,Post} */ - if (str_equiv(hook_event, NSSM_HOOK_EVENT_START)) { - if (str_equiv(hook_action, NSSM_HOOK_ACTION_PRE)) return true; - if (str_equiv(hook_action, NSSM_HOOK_ACTION_POST)) return true; - if (quiet) return false; - print_message(stderr, NSSM_MESSAGE_INVALID_HOOK_ACTION, hook_event); - _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_ACTION_PRE); - _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_ACTION_POST); - return false; - } - - /* Stop/Pre */ - if (str_equiv(hook_event, NSSM_HOOK_EVENT_STOP)) { - if (str_equiv(hook_action, NSSM_HOOK_ACTION_PRE)) return true; - if (quiet) return false; - print_message(stderr, NSSM_MESSAGE_INVALID_HOOK_ACTION, hook_event); - _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_ACTION_PRE); - return false; - } - - if (quiet) return false; - print_message(stderr, NSSM_MESSAGE_INVALID_HOOK_EVENT); - _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_EVENT_EXIT); - _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_EVENT_POWER); - _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_EVENT_ROTATE); - _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_EVENT_START); - _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_EVENT_STOP); - return false; -} - -void await_hook_threads(hook_thread_t *hook_threads, SERVICE_STATUS_HANDLE status_handle, SERVICE_STATUS *status, unsigned long deadline) { - if (! hook_threads) return; - if (! hook_threads->num_threads) return; - - int *retain = (int *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, hook_threads->num_threads * sizeof(int)); - if (! retain) { - log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("retain"), _T("await_hook_threads()"), 0); - return; - } - - /* - We could use WaitForMultipleObjects() but await_single_object() can update - the service status as well. - */ - int num_threads = 0; - int i; - for (i = 0; i < hook_threads->num_threads; i++) { - if (deadline) { - if (await_single_handle(status_handle, status, hook_threads->data[i].thread_handle, hook_threads->data[i].name, _T(__FUNCTION__), deadline) != 1) { - CloseHandle(hook_threads->data[i].thread_handle); - continue; - } - } - else if (WaitForSingleObject(hook_threads->data[i].thread_handle, 0) != WAIT_TIMEOUT) { - CloseHandle(hook_threads->data[i].thread_handle); - continue; - } - - retain[num_threads++]= i; - } - - if (num_threads) { - hook_thread_data_t *data = (hook_thread_data_t *) HeapAlloc(GetProcessHeap(), 0, num_threads * sizeof(hook_thread_data_t)); - if (! data) { - log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("data"), _T("await_hook_threads()"), 0); - HeapFree(GetProcessHeap(), 0, retain); - return; - } - - for (i = 0; i < num_threads; i++) memmove(&data[i], &hook_threads->data[retain[i]], sizeof(data[i])); - - HeapFree(GetProcessHeap(), 0, hook_threads->data); - hook_threads->data = data; - hook_threads->num_threads = num_threads; - } - else { - HeapFree(GetProcessHeap(), 0, hook_threads->data); - ZeroMemory(hook_threads, sizeof(*hook_threads)); - } - - HeapFree(GetProcessHeap(), 0, retain); -} - -/* - Returns: - NSSM_HOOK_STATUS_SUCCESS if the hook ran successfully. - NSSM_HOOK_STATUS_NOTFOUND if no hook was found. - NSSM_HOOK_STATUS_ABORT if the hook failed and we should cancel service start. - NSSM_HOOK_STATUS_ERROR on error. - NSSM_HOOK_STATUS_NOTRUN if the hook didn't run. - NSSM_HOOK_STATUS_TIMEOUT if the hook timed out. - NSSM_HOOK_STATUS_FAILED if the hook failed. -*/ -int nssm_hook(hook_thread_t *hook_threads, nssm_service_t *service, TCHAR *hook_event, TCHAR *hook_action, unsigned long *hook_control, unsigned long deadline, bool async) { - int ret = 0; - - hook_t *hook = (hook_t *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(hook_t)); - if (! hook) { - log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("hook"), _T("nssm_hook()"), 0); - return NSSM_HOOK_STATUS_ERROR; - } - - FILETIME now; - GetSystemTimeAsFileTime(&now); - - EnterCriticalSection(&service->hook_section); - - /* Set the environment. */ - set_service_environment(service); - - /* ABI version. */ - TCHAR number[16]; - _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%lu"), NSSM_HOOK_VERSION); - SetEnvironmentVariable(NSSM_HOOK_ENV_VERSION, number); - - /* Event triggering this action. */ - SetEnvironmentVariable(NSSM_HOOK_ENV_EVENT, hook_event); - - /* Hook action. */ - SetEnvironmentVariable(NSSM_HOOK_ENV_ACTION, hook_action); - - /* Control triggering this action. May be empty. */ - if (hook_control) SetEnvironmentVariable(NSSM_HOOK_ENV_TRIGGER, service_control_text(*hook_control)); - else SetEnvironmentVariable(NSSM_HOOK_ENV_TRIGGER, _T("")); - - /* Last control handled. */ - SetEnvironmentVariable(NSSM_HOOK_ENV_LAST_CONTROL, service_control_text(service->last_control)); - - /* Path to NSSM, unquoted for the environment. */ - SetEnvironmentVariable(NSSM_HOOK_ENV_IMAGE_PATH, nssm_unquoted_imagepath()); - - /* NSSM version. */ - SetEnvironmentVariable(NSSM_HOOK_ENV_NSSM_CONFIGURATION, NSSM_CONFIGURATION); - SetEnvironmentVariable(NSSM_HOOK_ENV_NSSM_VERSION, NSSM_VERSION); - SetEnvironmentVariable(NSSM_HOOK_ENV_BUILD_DATE, NSSM_DATE); - - /* NSSM PID. */ - _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%lu"), GetCurrentProcessId()); - SetEnvironmentVariable(NSSM_HOOK_ENV_PID, number); - - /* NSSM runtime. */ - set_hook_runtime(NSSM_HOOK_ENV_RUNTIME, &service->nssm_creation_time, &now); - - /* Application PID. */ - if (service->pid) { - _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%lu"), service->pid); - SetEnvironmentVariable(NSSM_HOOK_ENV_APPLICATION_PID, number); - /* Application runtime. */ - set_hook_runtime(NSSM_HOOK_ENV_APPLICATION_RUNTIME, &service->creation_time, &now); - /* Exit code. */ - SetEnvironmentVariable(NSSM_HOOK_ENV_EXITCODE, _T("")); - } - else { - SetEnvironmentVariable(NSSM_HOOK_ENV_APPLICATION_PID, _T("")); - if (str_equiv(hook_event, NSSM_HOOK_EVENT_START) && str_equiv(hook_action, NSSM_HOOK_ACTION_PRE)) { - SetEnvironmentVariable(NSSM_HOOK_ENV_APPLICATION_RUNTIME, _T("")); - SetEnvironmentVariable(NSSM_HOOK_ENV_EXITCODE, _T("")); - } - else { - set_hook_runtime(NSSM_HOOK_ENV_APPLICATION_RUNTIME, &service->creation_time, &service->exit_time); - /* Exit code. */ - _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%lu"), service->exitcode); - SetEnvironmentVariable(NSSM_HOOK_ENV_EXITCODE, number); - } - } - - /* Deadline for this script. */ - _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%lu"), deadline); - SetEnvironmentVariable(NSSM_HOOK_ENV_DEADLINE, number); - - /* Service name. */ - SetEnvironmentVariable(NSSM_HOOK_ENV_SERVICE_NAME, service->name); - SetEnvironmentVariable(NSSM_HOOK_ENV_SERVICE_DISPLAYNAME, service->displayname); - - /* Times the service was asked to start. */ - _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%lu"), service->start_requested_count); - SetEnvironmentVariable(NSSM_HOOK_ENV_START_REQUESTED_COUNT, number); - - /* Times the service actually did start. */ - _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%lu"), service->start_count); - SetEnvironmentVariable(NSSM_HOOK_ENV_START_COUNT, number); - - /* Times the service exited. */ - _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%lu"), service->exit_count); - SetEnvironmentVariable(NSSM_HOOK_ENV_EXIT_COUNT, number); - - /* Throttled count. */ - _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%lu"), service->throttle); - SetEnvironmentVariable(NSSM_HOOK_ENV_THROTTLE_COUNT, number); - - /* Command line. */ - TCHAR app[CMD_LENGTH]; - _sntprintf_s(app, _countof(app), _TRUNCATE, _T("\"%s\" %s"), service->exe, service->flags); - SetEnvironmentVariable(NSSM_HOOK_ENV_COMMAND_LINE, app); - - TCHAR cmd[CMD_LENGTH]; - if (get_hook(service->name, hook_event, hook_action, cmd, sizeof(cmd))) { - log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_HOOK_FAILED, hook_event, hook_action, service->name, 0); - unset_service_environment(service); - LeaveCriticalSection(&service->hook_section); - HeapFree(GetProcessHeap(), 0, hook); - return NSSM_HOOK_STATUS_ERROR; - } - - /* No hook. */ - if (! _tcslen(cmd)) { - unset_service_environment(service); - LeaveCriticalSection(&service->hook_section); - HeapFree(GetProcessHeap(), 0, hook); - return NSSM_HOOK_STATUS_NOTFOUND; - } - - /* Run the command. */ - STARTUPINFO si; - ZeroMemory(&si, sizeof(si)); - si.cb = sizeof(si); - PROCESS_INFORMATION pi; - ZeroMemory(&pi, sizeof(pi)); - unsigned long flags = 0; -#ifdef UNICODE - flags |= CREATE_UNICODE_ENVIRONMENT; -#endif - ret = NSSM_HOOK_STATUS_NOTRUN; - if (CreateProcess(0, cmd, 0, 0, false, flags, 0, service->dir, &si, &pi)) { - hook->name = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, HOOK_NAME_LENGTH * sizeof(TCHAR)); - if (hook->name) _sntprintf_s(hook->name, HOOK_NAME_LENGTH, _TRUNCATE, _T("%s (%s/%s)"), service->name, hook_event, hook_action); - hook->process_handle = pi.hProcess; - hook->pid = pi.dwProcessId; - hook->deadline = deadline; - if (get_process_creation_time(hook->process_handle, &hook->creation_time)) GetSystemTimeAsFileTime(&hook->creation_time); - - unsigned long tid; - HANDLE thread_handle = CreateThread(NULL, 0, await_hook, (void *) hook, 0, &tid); - if (thread_handle) { - if (async) { - ret = 0; - await_hook_threads(hook_threads, service->status_handle, &service->status, 0); - add_thread_handle(hook_threads, thread_handle, hook->name); - } - else { - await_single_handle(service->status_handle, &service->status, thread_handle, hook->name, _T(__FUNCTION__), deadline + NSSM_SERVICE_STATUS_DEADLINE); - unsigned long exitcode; - GetExitCodeThread(thread_handle, &exitcode); - ret = (int) exitcode; - CloseHandle(thread_handle); - } - } - else { - log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0); - await_hook(hook); - if (hook->name) HeapFree(GetProcessHeap(), 0, hook->name); - HeapFree(GetProcessHeap(), 0, hook); - } - } - else { - log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_HOOK_CREATEPROCESS_FAILED, hook_event, hook_action, service->name, cmd, error_string(GetLastError()), 0); - HeapFree(GetProcessHeap(), 0, hook); - } - - /* Restore our environment. */ - unset_service_environment(service); - - LeaveCriticalSection(&service->hook_section); - - return ret; -} - -int nssm_hook(hook_thread_t *hook_threads, nssm_service_t *service, TCHAR *hook_event, TCHAR *hook_action, unsigned long *hook_control, unsigned long deadline) { - return nssm_hook(hook_threads, service, hook_event, hook_action, hook_control, deadline, true); -} - -int nssm_hook(hook_thread_t *hook_threads, nssm_service_t *service, TCHAR *hook_event, TCHAR *hook_action, unsigned long *hook_control) { - return nssm_hook(hook_threads, service, hook_event, hook_action, hook_control, NSSM_HOOK_DEADLINE); -} +#include "nssm.h" + +typedef struct { + TCHAR *name; + HANDLE process_handle; + unsigned long pid; + unsigned long deadline; + FILETIME creation_time; + kill_t k; +} hook_t; + +static unsigned long WINAPI await_hook(void *arg) { + hook_t *hook = (hook_t *) arg; + if (! hook) return NSSM_HOOK_STATUS_ERROR; + + int ret = 0; + if (WaitForSingleObject(hook->process_handle, hook->deadline) == WAIT_TIMEOUT) ret = NSSM_HOOK_STATUS_TIMEOUT; + + /* Tidy up hook process tree. */ + if (hook->name) hook->k.name = hook->name; + else hook->k.name = _T("hook"); + hook->k.process_handle = hook->process_handle; + hook->k.pid = hook->pid; + hook->k.stop_method = ~0; + hook->k.kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD; + hook->k.kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD; + hook->k.kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD; + hook->k.creation_time = hook->creation_time; + GetSystemTimeAsFileTime(&hook->k.exit_time); + kill_process_tree(&hook->k, hook->pid); + + if (ret) { + CloseHandle(hook->process_handle); + if (hook->name) HeapFree(GetProcessHeap(), 0, hook->name); + HeapFree(GetProcessHeap(), 0, hook); + return ret; + } + + unsigned long exitcode; + GetExitCodeProcess(hook->process_handle, &exitcode); + CloseHandle(hook->process_handle); + + if (hook->name) HeapFree(GetProcessHeap(), 0, hook->name); + HeapFree(GetProcessHeap(), 0, hook); + + if (exitcode == NSSM_HOOK_STATUS_ABORT) return NSSM_HOOK_STATUS_ABORT; + if (exitcode) return NSSM_HOOK_STATUS_FAILED; + + return NSSM_HOOK_STATUS_SUCCESS; +} + +static void set_hook_runtime(TCHAR *v, FILETIME *start, FILETIME *now) { + if (start && now) { + ULARGE_INTEGER s; + s.LowPart = start->dwLowDateTime; + s.HighPart = start->dwHighDateTime; + if (s.QuadPart) { + ULARGE_INTEGER t; + t.LowPart = now->dwLowDateTime; + t.HighPart = now->dwHighDateTime; + if (t.QuadPart && t.QuadPart >= s.QuadPart) { + t.QuadPart -= s.QuadPart; + t.QuadPart /= 10000LL; + TCHAR number[16]; + _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%llu"), t.QuadPart); + SetEnvironmentVariable(v, number); + return; + } + } + } + SetEnvironmentVariable(v, _T("")); +} + +static void add_thread_handle(hook_thread_t *hook_threads, HANDLE thread_handle, TCHAR *name) { + if (! hook_threads) return; + + int num_threads = hook_threads->num_threads + 1; + hook_thread_data_t *data = (hook_thread_data_t *) HeapAlloc(GetProcessHeap(), 0, num_threads * sizeof(hook_thread_data_t)); + if (! data) { + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("hook_thread_t"), _T("add_thread_handle()"), 0); + return; + } + + int i; + for (i = 0; i < hook_threads->num_threads; i++) memmove(&data[i], &hook_threads->data[i], sizeof(data[i])); + memmove(data[i].name, name, sizeof(data[i].name)); + data[i].thread_handle = thread_handle; + + if (hook_threads->data) HeapFree(GetProcessHeap(), 0, hook_threads->data); + hook_threads->data = data; + hook_threads->num_threads = num_threads; +} + +bool valid_hook_name(const TCHAR *hook_event, const TCHAR *hook_action, bool quiet) { + bool valid_event = false; + bool valid_action = false; + + /* Exit/Post */ + if (str_equiv(hook_event, NSSM_HOOK_EVENT_EXIT)) { + if (str_equiv(hook_action, NSSM_HOOK_ACTION_POST)) return true; + if (quiet) return false; + print_message(stderr, NSSM_MESSAGE_INVALID_HOOK_ACTION, hook_event); + _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_ACTION_POST); + return false; + } + + /* Power/{Change,Resume} */ + if (str_equiv(hook_event, NSSM_HOOK_EVENT_POWER)) { + if (str_equiv(hook_action, NSSM_HOOK_ACTION_CHANGE)) return true; + if (str_equiv(hook_action, NSSM_HOOK_ACTION_RESUME)) return true; + if (quiet) return false; + print_message(stderr, NSSM_MESSAGE_INVALID_HOOK_ACTION, hook_event); + _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_ACTION_CHANGE); + _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_ACTION_RESUME); + return false; + } + + /* Rotate/{Pre,Post} */ + if (str_equiv(hook_event, NSSM_HOOK_EVENT_ROTATE)) { + if (str_equiv(hook_action, NSSM_HOOK_ACTION_PRE)) return true; + if (str_equiv(hook_action, NSSM_HOOK_ACTION_POST)) return true; + if (quiet) return false; + print_message(stderr, NSSM_MESSAGE_INVALID_HOOK_ACTION, hook_event); + _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_ACTION_PRE); + _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_ACTION_POST); + return false; + } + + /* Start/{Pre,Post} */ + if (str_equiv(hook_event, NSSM_HOOK_EVENT_START)) { + if (str_equiv(hook_action, NSSM_HOOK_ACTION_PRE)) return true; + if (str_equiv(hook_action, NSSM_HOOK_ACTION_POST)) return true; + if (quiet) return false; + print_message(stderr, NSSM_MESSAGE_INVALID_HOOK_ACTION, hook_event); + _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_ACTION_PRE); + _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_ACTION_POST); + return false; + } + + /* Stop/Pre */ + if (str_equiv(hook_event, NSSM_HOOK_EVENT_STOP)) { + if (str_equiv(hook_action, NSSM_HOOK_ACTION_PRE)) return true; + if (quiet) return false; + print_message(stderr, NSSM_MESSAGE_INVALID_HOOK_ACTION, hook_event); + _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_ACTION_PRE); + return false; + } + + if (quiet) return false; + print_message(stderr, NSSM_MESSAGE_INVALID_HOOK_EVENT); + _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_EVENT_EXIT); + _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_EVENT_POWER); + _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_EVENT_ROTATE); + _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_EVENT_START); + _ftprintf(stderr, _T("%s\n"), NSSM_HOOK_EVENT_STOP); + return false; +} + +void await_hook_threads(hook_thread_t *hook_threads, SERVICE_STATUS_HANDLE status_handle, SERVICE_STATUS *status, unsigned long deadline) { + if (! hook_threads) return; + if (! hook_threads->num_threads) return; + + int *retain = (int *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, hook_threads->num_threads * sizeof(int)); + if (! retain) { + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("retain"), _T("await_hook_threads()"), 0); + return; + } + + /* + We could use WaitForMultipleObjects() but await_single_object() can update + the service status as well. + */ + int num_threads = 0; + int i; + for (i = 0; i < hook_threads->num_threads; i++) { + if (deadline) { + if (await_single_handle(status_handle, status, hook_threads->data[i].thread_handle, hook_threads->data[i].name, _T(__FUNCTION__), deadline) != 1) { + CloseHandle(hook_threads->data[i].thread_handle); + continue; + } + } + else if (WaitForSingleObject(hook_threads->data[i].thread_handle, 0) != WAIT_TIMEOUT) { + CloseHandle(hook_threads->data[i].thread_handle); + continue; + } + + retain[num_threads++]= i; + } + + if (num_threads) { + hook_thread_data_t *data = (hook_thread_data_t *) HeapAlloc(GetProcessHeap(), 0, num_threads * sizeof(hook_thread_data_t)); + if (! data) { + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("data"), _T("await_hook_threads()"), 0); + HeapFree(GetProcessHeap(), 0, retain); + return; + } + + for (i = 0; i < num_threads; i++) memmove(&data[i], &hook_threads->data[retain[i]], sizeof(data[i])); + + HeapFree(GetProcessHeap(), 0, hook_threads->data); + hook_threads->data = data; + hook_threads->num_threads = num_threads; + } + else { + HeapFree(GetProcessHeap(), 0, hook_threads->data); + ZeroMemory(hook_threads, sizeof(*hook_threads)); + } + + HeapFree(GetProcessHeap(), 0, retain); +} + +/* + Returns: + NSSM_HOOK_STATUS_SUCCESS if the hook ran successfully. + NSSM_HOOK_STATUS_NOTFOUND if no hook was found. + NSSM_HOOK_STATUS_ABORT if the hook failed and we should cancel service start. + NSSM_HOOK_STATUS_ERROR on error. + NSSM_HOOK_STATUS_NOTRUN if the hook didn't run. + NSSM_HOOK_STATUS_TIMEOUT if the hook timed out. + NSSM_HOOK_STATUS_FAILED if the hook failed. +*/ +int nssm_hook(hook_thread_t *hook_threads, nssm_service_t *service, TCHAR *hook_event, TCHAR *hook_action, unsigned long *hook_control, unsigned long deadline, bool async) { + int ret = 0; + + hook_t *hook = (hook_t *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(hook_t)); + if (! hook) { + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("hook"), _T("nssm_hook()"), 0); + return NSSM_HOOK_STATUS_ERROR; + } + + FILETIME now; + GetSystemTimeAsFileTime(&now); + + EnterCriticalSection(&service->hook_section); + + /* Set the environment. */ + set_service_environment(service); + + /* ABI version. */ + TCHAR number[16]; + _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%lu"), NSSM_HOOK_VERSION); + SetEnvironmentVariable(NSSM_HOOK_ENV_VERSION, number); + + /* Event triggering this action. */ + SetEnvironmentVariable(NSSM_HOOK_ENV_EVENT, hook_event); + + /* Hook action. */ + SetEnvironmentVariable(NSSM_HOOK_ENV_ACTION, hook_action); + + /* Control triggering this action. May be empty. */ + if (hook_control) SetEnvironmentVariable(NSSM_HOOK_ENV_TRIGGER, service_control_text(*hook_control)); + else SetEnvironmentVariable(NSSM_HOOK_ENV_TRIGGER, _T("")); + + /* Last control handled. */ + SetEnvironmentVariable(NSSM_HOOK_ENV_LAST_CONTROL, service_control_text(service->last_control)); + + /* Path to NSSM, unquoted for the environment. */ + SetEnvironmentVariable(NSSM_HOOK_ENV_IMAGE_PATH, nssm_unquoted_imagepath()); + + /* NSSM version. */ + SetEnvironmentVariable(NSSM_HOOK_ENV_NSSM_CONFIGURATION, NSSM_CONFIGURATION); + SetEnvironmentVariable(NSSM_HOOK_ENV_NSSM_VERSION, NSSM_VERSION); + SetEnvironmentVariable(NSSM_HOOK_ENV_BUILD_DATE, NSSM_DATE); + + /* NSSM PID. */ + _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%lu"), GetCurrentProcessId()); + SetEnvironmentVariable(NSSM_HOOK_ENV_PID, number); + + /* NSSM runtime. */ + set_hook_runtime(NSSM_HOOK_ENV_RUNTIME, &service->nssm_creation_time, &now); + + /* Application PID. */ + if (service->pid) { + _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%lu"), service->pid); + SetEnvironmentVariable(NSSM_HOOK_ENV_APPLICATION_PID, number); + /* Application runtime. */ + set_hook_runtime(NSSM_HOOK_ENV_APPLICATION_RUNTIME, &service->creation_time, &now); + /* Exit code. */ + SetEnvironmentVariable(NSSM_HOOK_ENV_EXITCODE, _T("")); + } + else { + SetEnvironmentVariable(NSSM_HOOK_ENV_APPLICATION_PID, _T("")); + if (str_equiv(hook_event, NSSM_HOOK_EVENT_START) && str_equiv(hook_action, NSSM_HOOK_ACTION_PRE)) { + SetEnvironmentVariable(NSSM_HOOK_ENV_APPLICATION_RUNTIME, _T("")); + SetEnvironmentVariable(NSSM_HOOK_ENV_EXITCODE, _T("")); + } + else { + set_hook_runtime(NSSM_HOOK_ENV_APPLICATION_RUNTIME, &service->creation_time, &service->exit_time); + /* Exit code. */ + _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%lu"), service->exitcode); + SetEnvironmentVariable(NSSM_HOOK_ENV_EXITCODE, number); + } + } + + /* Deadline for this script. */ + _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%lu"), deadline); + SetEnvironmentVariable(NSSM_HOOK_ENV_DEADLINE, number); + + /* Service name. */ + SetEnvironmentVariable(NSSM_HOOK_ENV_SERVICE_NAME, service->name); + SetEnvironmentVariable(NSSM_HOOK_ENV_SERVICE_DISPLAYNAME, service->displayname); + + /* Times the service was asked to start. */ + _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%lu"), service->start_requested_count); + SetEnvironmentVariable(NSSM_HOOK_ENV_START_REQUESTED_COUNT, number); + + /* Times the service actually did start. */ + _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%lu"), service->start_count); + SetEnvironmentVariable(NSSM_HOOK_ENV_START_COUNT, number); + + /* Times the service exited. */ + _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%lu"), service->exit_count); + SetEnvironmentVariable(NSSM_HOOK_ENV_EXIT_COUNT, number); + + /* Throttled count. */ + _sntprintf_s(number, _countof(number), _TRUNCATE, _T("%lu"), service->throttle); + SetEnvironmentVariable(NSSM_HOOK_ENV_THROTTLE_COUNT, number); + + /* Command line. */ + TCHAR app[CMD_LENGTH]; + _sntprintf_s(app, _countof(app), _TRUNCATE, _T("\"%s\" %s"), service->exe, service->flags); + SetEnvironmentVariable(NSSM_HOOK_ENV_COMMAND_LINE, app); + + TCHAR cmd[CMD_LENGTH]; + if (get_hook(service->name, hook_event, hook_action, cmd, sizeof(cmd))) { + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_HOOK_FAILED, hook_event, hook_action, service->name, 0); + unset_service_environment(service); + LeaveCriticalSection(&service->hook_section); + HeapFree(GetProcessHeap(), 0, hook); + return NSSM_HOOK_STATUS_ERROR; + } + + /* No hook. */ + if (! _tcslen(cmd)) { + unset_service_environment(service); + LeaveCriticalSection(&service->hook_section); + HeapFree(GetProcessHeap(), 0, hook); + return NSSM_HOOK_STATUS_NOTFOUND; + } + + /* Run the command. */ + STARTUPINFO si; + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + PROCESS_INFORMATION pi; + ZeroMemory(&pi, sizeof(pi)); + unsigned long flags = 0; +#ifdef UNICODE + flags |= CREATE_UNICODE_ENVIRONMENT; +#endif + ret = NSSM_HOOK_STATUS_NOTRUN; + if (CreateProcess(0, cmd, 0, 0, false, flags, 0, service->dir, &si, &pi)) { + hook->name = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, HOOK_NAME_LENGTH * sizeof(TCHAR)); + if (hook->name) _sntprintf_s(hook->name, HOOK_NAME_LENGTH, _TRUNCATE, _T("%s (%s/%s)"), service->name, hook_event, hook_action); + hook->process_handle = pi.hProcess; + hook->pid = pi.dwProcessId; + hook->deadline = deadline; + if (get_process_creation_time(hook->process_handle, &hook->creation_time)) GetSystemTimeAsFileTime(&hook->creation_time); + + unsigned long tid; + HANDLE thread_handle = CreateThread(NULL, 0, await_hook, (void *) hook, 0, &tid); + if (thread_handle) { + if (async) { + ret = 0; + await_hook_threads(hook_threads, service->status_handle, &service->status, 0); + add_thread_handle(hook_threads, thread_handle, hook->name); + } + else { + await_single_handle(service->status_handle, &service->status, thread_handle, hook->name, _T(__FUNCTION__), deadline + NSSM_SERVICE_STATUS_DEADLINE); + unsigned long exitcode; + GetExitCodeThread(thread_handle, &exitcode); + ret = (int) exitcode; + CloseHandle(thread_handle); + } + } + else { + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0); + await_hook(hook); + if (hook->name) HeapFree(GetProcessHeap(), 0, hook->name); + HeapFree(GetProcessHeap(), 0, hook); + } + } + else { + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_HOOK_CREATEPROCESS_FAILED, hook_event, hook_action, service->name, cmd, error_string(GetLastError()), 0); + HeapFree(GetProcessHeap(), 0, hook); + } + + /* Restore our environment. */ + unset_service_environment(service); + + LeaveCriticalSection(&service->hook_section); + + return ret; +} + +int nssm_hook(hook_thread_t *hook_threads, nssm_service_t *service, TCHAR *hook_event, TCHAR *hook_action, unsigned long *hook_control, unsigned long deadline) { + return nssm_hook(hook_threads, service, hook_event, hook_action, hook_control, deadline, true); +} + +int nssm_hook(hook_thread_t *hook_threads, nssm_service_t *service, TCHAR *hook_event, TCHAR *hook_action, unsigned long *hook_control) { + return nssm_hook(hook_threads, service, hook_event, hook_action, hook_control, NSSM_HOOK_DEADLINE); +} diff --git a/hook.h b/hook.h index 045e4aa..85e211e 100644 --- a/hook.h +++ b/hook.h @@ -1,75 +1,75 @@ -#ifndef HOOK_H -#define HOOK_H - -#define NSSM_HOOK_EVENT_START _T("Start") -#define NSSM_HOOK_EVENT_STOP _T("Stop") -#define NSSM_HOOK_EVENT_EXIT _T("Exit") -#define NSSM_HOOK_EVENT_POWER _T("Power") -#define NSSM_HOOK_EVENT_ROTATE _T("Rotate") - -#define NSSM_HOOK_ACTION_PRE _T("Pre") -#define NSSM_HOOK_ACTION_POST _T("Post") -#define NSSM_HOOK_ACTION_CHANGE _T("Change") -#define NSSM_HOOK_ACTION_RESUME _T("Resume") - -/* Hook name will be " (/)" */ -#define HOOK_NAME_LENGTH SERVICE_NAME_LENGTH * 2 - -#define NSSM_HOOK_VERSION 1 - -/* Hook ran successfully. */ -#define NSSM_HOOK_STATUS_SUCCESS 0 -/* No hook configured. */ -#define NSSM_HOOK_STATUS_NOTFOUND 1 -/* Hook requested abort. */ -#define NSSM_HOOK_STATUS_ABORT 99 -/* Internal error launching hook. */ -#define NSSM_HOOK_STATUS_ERROR 100 -/* Hook was not run. */ -#define NSSM_HOOK_STATUS_NOTRUN 101 -/* Hook timed out. */ -#define NSSM_HOOK_STATUS_TIMEOUT 102 -/* Hook returned non-zero. */ -#define NSSM_HOOK_STATUS_FAILED 111 - -/* Version 1. */ -#define NSSM_HOOK_ENV_VERSION _T("NSSM_HOOK_VERSION") -#define NSSM_HOOK_ENV_IMAGE_PATH _T("NSSM_EXE") -#define NSSM_HOOK_ENV_NSSM_CONFIGURATION _T("NSSM_CONFIGURATION") -#define NSSM_HOOK_ENV_NSSM_VERSION _T("NSSM_VERSION") -#define NSSM_HOOK_ENV_BUILD_DATE _T("NSSM_BUILD_DATE") -#define NSSM_HOOK_ENV_PID _T("NSSM_PID") -#define NSSM_HOOK_ENV_DEADLINE _T("NSSM_DEADLINE") -#define NSSM_HOOK_ENV_SERVICE_NAME _T("NSSM_SERVICE_NAME") -#define NSSM_HOOK_ENV_SERVICE_DISPLAYNAME _T("NSSM_SERVICE_DISPLAYNAME") -#define NSSM_HOOK_ENV_COMMAND_LINE _T("NSSM_COMMAND_LINE") -#define NSSM_HOOK_ENV_APPLICATION_PID _T("NSSM_APPLICATION_PID") -#define NSSM_HOOK_ENV_EVENT _T("NSSM_EVENT") -#define NSSM_HOOK_ENV_ACTION _T("NSSM_ACTION") -#define NSSM_HOOK_ENV_TRIGGER _T("NSSM_TRIGGER") -#define NSSM_HOOK_ENV_LAST_CONTROL _T("NSSM_LAST_CONTROL") -#define NSSM_HOOK_ENV_START_REQUESTED_COUNT _T("NSSM_START_REQUESTED_COUNT") -#define NSSM_HOOK_ENV_START_COUNT _T("NSSM_START_COUNT") -#define NSSM_HOOK_ENV_THROTTLE_COUNT _T("NSSM_THROTTLE_COUNT") -#define NSSM_HOOK_ENV_EXIT_COUNT _T("NSSM_EXIT_COUNT") -#define NSSM_HOOK_ENV_EXITCODE _T("NSSM_EXITCODE") -#define NSSM_HOOK_ENV_RUNTIME _T("NSSM_RUNTIME") -#define NSSM_HOOK_ENV_APPLICATION_RUNTIME _T("NSSM_APPLICATION_RUNTIME") - -typedef struct { - TCHAR name[HOOK_NAME_LENGTH]; - HANDLE thread_handle; -} hook_thread_data_t; - -typedef struct { - hook_thread_data_t *data; - int num_threads; -} hook_thread_t; - -bool valid_hook_name(const TCHAR *, const TCHAR *, bool); -void await_hook_threads(hook_thread_t *, SERVICE_STATUS_HANDLE, SERVICE_STATUS *, unsigned long); -int nssm_hook(hook_thread_t *, nssm_service_t *, TCHAR *, TCHAR *, unsigned long *, unsigned long, bool); -int nssm_hook(hook_thread_t *, nssm_service_t *, TCHAR *, TCHAR *, unsigned long *, unsigned long); -int nssm_hook(hook_thread_t *, nssm_service_t *, TCHAR *, TCHAR *, unsigned long *); - -#endif +#ifndef HOOK_H +#define HOOK_H + +#define NSSM_HOOK_EVENT_START _T("Start") +#define NSSM_HOOK_EVENT_STOP _T("Stop") +#define NSSM_HOOK_EVENT_EXIT _T("Exit") +#define NSSM_HOOK_EVENT_POWER _T("Power") +#define NSSM_HOOK_EVENT_ROTATE _T("Rotate") + +#define NSSM_HOOK_ACTION_PRE _T("Pre") +#define NSSM_HOOK_ACTION_POST _T("Post") +#define NSSM_HOOK_ACTION_CHANGE _T("Change") +#define NSSM_HOOK_ACTION_RESUME _T("Resume") + +/* Hook name will be " (/)" */ +#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 diff --git a/imports.cpp b/imports.cpp index 6257131..ec3a2a8 100644 --- a/imports.cpp +++ b/imports.cpp @@ -1,92 +1,92 @@ -#include "nssm.h" - -imports_t imports; - -/* - Try to set up function pointers. - In this first implementation it is not an error if we can't load them - because we aren't currently trying to load any functions which we - absolutely need. If we later add some indispensible imports we can - return non-zero here to force an application exit. -*/ -HMODULE get_dll(const TCHAR *dll, unsigned long *error) { - *error = 0; - - HMODULE ret = LoadLibrary(dll); - if (! ret) { - *error = GetLastError(); - log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_LOADLIBRARY_FAILED, dll, error_string(*error), 0); - } - - return ret; -} - -FARPROC get_import(HMODULE library, const char *function, unsigned long *error) { - *error = 0; - - FARPROC ret = GetProcAddress(library, function); - if (! ret) { - *error = GetLastError(); - TCHAR *function_name; -#ifdef UNICODE - size_t buflen; - mbstowcs_s(&buflen, NULL, 0, function, _TRUNCATE); - function_name = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, buflen * sizeof(TCHAR)); - if (function_name) mbstowcs_s(&buflen, function_name, buflen * sizeof(TCHAR), function, _TRUNCATE); -#else - function_name = (TCHAR *) function; -#endif - if (*error != ERROR_PROC_NOT_FOUND) log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_GETPROCADDRESS_FAILED, function_name, error_string(*error), 0); -#ifdef UNICODE - if (function_name) HeapFree(GetProcessHeap(), 0, function_name); -#endif - } - - return ret; -} - -int get_imports() { - unsigned long error; - - ZeroMemory(&imports, sizeof(imports)); - - imports.kernel32 = get_dll(_T("kernel32.dll"), &error); - if (imports.kernel32) { - imports.AttachConsole = (AttachConsole_ptr) get_import(imports.kernel32, "AttachConsole", &error); - if (! imports.AttachConsole) { - if (error != ERROR_PROC_NOT_FOUND) return 2; - } - - imports.SleepConditionVariableCS = (SleepConditionVariableCS_ptr) get_import(imports.kernel32, "SleepConditionVariableCS", &error); - if (! imports.SleepConditionVariableCS) { - if (error != ERROR_PROC_NOT_FOUND) return 3; - } - - imports.WakeConditionVariable = (WakeConditionVariable_ptr) get_import(imports.kernel32, "WakeConditionVariable", &error); - if (! imports.WakeConditionVariable) { - if (error != ERROR_PROC_NOT_FOUND) return 4; - } - } - else if (error != ERROR_MOD_NOT_FOUND) return 1; - - imports.advapi32 = get_dll(_T("advapi32.dll"), &error); - if (imports.advapi32) { - imports.CreateWellKnownSid = (CreateWellKnownSid_ptr) get_import(imports.advapi32, "CreateWellKnownSid", &error); - if (! imports.CreateWellKnownSid) { - if (error != ERROR_PROC_NOT_FOUND) return 6; - } - imports.IsWellKnownSid = (IsWellKnownSid_ptr) get_import(imports.advapi32, "IsWellKnownSid", &error); - if (! imports.IsWellKnownSid) { - if (error != ERROR_PROC_NOT_FOUND) return 7; - } - } - else if (error != ERROR_MOD_NOT_FOUND) return 5; - - return 0; -} - -void free_imports() { - if (imports.kernel32) FreeLibrary(imports.kernel32); - if (imports.advapi32) FreeLibrary(imports.advapi32); - ZeroMemory(&imports, sizeof(imports)); -} +#include "nssm.h" + +imports_t imports; + +/* + Try to set up function pointers. + In this first implementation it is not an error if we can't load them + because we aren't currently trying to load any functions which we + absolutely need. If we later add some indispensible imports we can + return non-zero here to force an application exit. +*/ +HMODULE get_dll(const TCHAR *dll, unsigned long *error) { + *error = 0; + + HMODULE ret = LoadLibrary(dll); + if (! ret) { + *error = GetLastError(); + log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_LOADLIBRARY_FAILED, dll, error_string(*error), 0); + } + + return ret; +} + +FARPROC get_import(HMODULE library, const char *function, unsigned long *error) { + *error = 0; + + FARPROC ret = GetProcAddress(library, function); + if (! ret) { + *error = GetLastError(); + TCHAR *function_name; +#ifdef UNICODE + size_t buflen; + mbstowcs_s(&buflen, NULL, 0, function, _TRUNCATE); + function_name = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, buflen * sizeof(TCHAR)); + if (function_name) mbstowcs_s(&buflen, function_name, buflen * sizeof(TCHAR), function, _TRUNCATE); +#else + function_name = (TCHAR *) function; +#endif + if (*error != ERROR_PROC_NOT_FOUND) log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_GETPROCADDRESS_FAILED, function_name, error_string(*error), 0); +#ifdef UNICODE + if (function_name) HeapFree(GetProcessHeap(), 0, function_name); +#endif + } + + return ret; +} + +int get_imports() { + unsigned long error; + + ZeroMemory(&imports, sizeof(imports)); + + imports.kernel32 = get_dll(_T("kernel32.dll"), &error); + if (imports.kernel32) { + imports.AttachConsole = (AttachConsole_ptr) get_import(imports.kernel32, "AttachConsole", &error); + if (! imports.AttachConsole) { + if (error != ERROR_PROC_NOT_FOUND) return 2; + } + + imports.SleepConditionVariableCS = (SleepConditionVariableCS_ptr) get_import(imports.kernel32, "SleepConditionVariableCS", &error); + if (! imports.SleepConditionVariableCS) { + if (error != ERROR_PROC_NOT_FOUND) return 3; + } + + imports.WakeConditionVariable = (WakeConditionVariable_ptr) get_import(imports.kernel32, "WakeConditionVariable", &error); + if (! imports.WakeConditionVariable) { + if (error != ERROR_PROC_NOT_FOUND) return 4; + } + } + else if (error != ERROR_MOD_NOT_FOUND) return 1; + + imports.advapi32 = get_dll(_T("advapi32.dll"), &error); + if (imports.advapi32) { + imports.CreateWellKnownSid = (CreateWellKnownSid_ptr) get_import(imports.advapi32, "CreateWellKnownSid", &error); + if (! imports.CreateWellKnownSid) { + if (error != ERROR_PROC_NOT_FOUND) return 6; + } + imports.IsWellKnownSid = (IsWellKnownSid_ptr) get_import(imports.advapi32, "IsWellKnownSid", &error); + if (! imports.IsWellKnownSid) { + if (error != ERROR_PROC_NOT_FOUND) return 7; + } + } + else if (error != ERROR_MOD_NOT_FOUND) return 5; + + return 0; +} + +void free_imports() { + if (imports.kernel32) FreeLibrary(imports.kernel32); + if (imports.advapi32) FreeLibrary(imports.advapi32); + ZeroMemory(&imports, sizeof(imports)); +} diff --git a/imports.h b/imports.h index 03e2df1..008a702 100644 --- a/imports.h +++ b/imports.h @@ -1,25 +1,25 @@ -#ifndef IMPORTS_H -#define IMPORTS_H - -typedef BOOL (WINAPI *AttachConsole_ptr)(DWORD); -typedef BOOL (WINAPI *SleepConditionVariableCS_ptr)(PCONDITION_VARIABLE, PCRITICAL_SECTION, DWORD); -typedef void (WINAPI *WakeConditionVariable_ptr)(PCONDITION_VARIABLE); -typedef BOOL (WINAPI *CreateWellKnownSid_ptr)(WELL_KNOWN_SID_TYPE, SID *, SID *, unsigned long *); -typedef BOOL (WINAPI *IsWellKnownSid_ptr)(SID *, WELL_KNOWN_SID_TYPE); - -typedef struct { - HMODULE kernel32; - HMODULE advapi32; - AttachConsole_ptr AttachConsole; - SleepConditionVariableCS_ptr SleepConditionVariableCS; - WakeConditionVariable_ptr WakeConditionVariable; - CreateWellKnownSid_ptr CreateWellKnownSid; - IsWellKnownSid_ptr IsWellKnownSid; -} imports_t; - -HMODULE get_dll(const TCHAR *, unsigned long *); -FARPROC get_import(HMODULE, const char *, unsigned long *); -int get_imports(); -void free_imports(); - -#endif +#ifndef IMPORTS_H +#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 diff --git a/messages.mc b/messages.mc index 985a4b4..14ce8df 100644 Binary files a/messages.mc and b/messages.mc differ diff --git a/process.cpp b/process.cpp index b03b504..d1a9a49 100644 --- a/process.cpp +++ b/process.cpp @@ -1,350 +1,350 @@ -#include "nssm.h" - -extern imports_t imports; - -void service_kill_t(nssm_service_t *service, kill_t *k) { - if (! service) return; - if (! k) return; - - ZeroMemory(k, sizeof(*k)); - k->name = service->name; - k->process_handle = service->process_handle; - k->pid = service->pid; - k->exitcode = service->exitcode; - k->stop_method = service->stop_method; - k->kill_console_delay = service->kill_console_delay; - k->kill_window_delay = service->kill_window_delay; - k->kill_threads_delay = service->kill_threads_delay; - k->status_handle = service->status_handle; - k->status = &service->status; - k->creation_time = service->creation_time; - k->exit_time = service->exit_time; -} - -int get_process_creation_time(HANDLE process_handle, FILETIME *ft) { - FILETIME creation_time, exit_time, kernel_time, user_time; - - if (! GetProcessTimes(process_handle, &creation_time, &exit_time, &kernel_time, &user_time)) { - log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GETPROCESSTIMES_FAILED, error_string(GetLastError()), 0); - return 1; - } - - memmove(ft, &creation_time, sizeof(creation_time)); - - return 0; -} - -int get_process_exit_time(HANDLE process_handle, FILETIME *ft) { - FILETIME creation_time, exit_time, kernel_time, user_time; - - if (! GetProcessTimes(process_handle, &creation_time, &exit_time, &kernel_time, &user_time)) { - log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GETPROCESSTIMES_FAILED, error_string(GetLastError()), 0); - return 1; - } - - if (! (exit_time.dwLowDateTime || exit_time.dwHighDateTime)) return 2; - memmove(ft, &exit_time, sizeof(exit_time)); - - return 0; -} - -int check_parent(kill_t *k, PROCESSENTRY32 *pe, unsigned long ppid) { - /* Check parent process ID matches. */ - if (pe->th32ParentProcessID != ppid) return 1; - - /* - Process IDs can be reused so do a sanity check by making sure the child - has been running for less time than the parent. - Though unlikely, it's possible that the parent exited and its process ID - was already reused, so we'll also compare against its exit time. - */ - HANDLE process_handle = OpenProcess(PROCESS_QUERY_INFORMATION, false, pe->th32ProcessID); - if (! process_handle) { - TCHAR pid_string[16]; - _sntprintf_s(pid_string, _countof(pid_string), _TRUNCATE, _T("%lu"), pe->th32ProcessID); - log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENPROCESS_FAILED, pid_string, k->name, error_string(GetLastError()), 0); - return 2; - } - - FILETIME ft; - if (get_process_creation_time(process_handle, &ft)) { - CloseHandle(process_handle); - return 3; - } - - CloseHandle(process_handle); - - /* Verify that the parent's creation time is not later. */ - if (CompareFileTime(&k->creation_time, &ft) > 0) return 4; - - /* Verify that the parent's exit time is not earlier. */ - if (CompareFileTime(&k->exit_time, &ft) < 0) return 5; - - return 0; -} - -/* Send some window messages and hope the window respects one or more. */ -int CALLBACK kill_window(HWND window, LPARAM arg) { - kill_t *k = (kill_t *) arg; - - unsigned long pid; - if (! GetWindowThreadProcessId(window, &pid)) return 1; - if (pid != k->pid) return 1; - - /* First try sending WM_CLOSE to request that the window close. */ - k->signalled |= PostMessage(window, WM_CLOSE, k->exitcode, 0); - - /* - Then tell the window that the user is logging off and it should exit - without worrying about saving any data. - */ - k->signalled |= PostMessage(window, WM_ENDSESSION, 1, ENDSESSION_CLOSEAPP | ENDSESSION_CRITICAL | ENDSESSION_LOGOFF); - - return 1; -} - -/* - Try to post a message to the message queues of threads associated with the - given process ID. Not all threads have message queues so there's no - guarantee of success, and we don't want to be left waiting for unsignalled - processes so this function returns only true if at least one thread was - successfully prodded. -*/ -int kill_threads(nssm_service_t *service, kill_t *k) { - int ret = 0; - - /* Get a snapshot of all threads in the system. */ - HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); - if (! snapshot) { - log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETOOLHELP32SNAPSHOT_THREAD_FAILED, k->name, error_string(GetLastError()), 0); - return 0; - } - - THREADENTRY32 te; - ZeroMemory(&te, sizeof(te)); - te.dwSize = sizeof(te); - - if (! Thread32First(snapshot, &te)) { - log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_THREAD_ENUMERATE_FAILED, k->name, error_string(GetLastError()), 0); - CloseHandle(snapshot); - return 0; - } - - /* This thread belongs to the doomed process so signal it. */ - if (te.th32OwnerProcessID == k->pid) { - ret |= PostThreadMessage(te.th32ThreadID, WM_QUIT, k->exitcode, 0); - } - - while (true) { - /* Try to get the next thread. */ - if (! Thread32Next(snapshot, &te)) { - unsigned long error = GetLastError(); - if (error == ERROR_NO_MORE_FILES) break; - log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_THREAD_ENUMERATE_FAILED, k->name, error_string(GetLastError()), 0); - CloseHandle(snapshot); - return ret; - } - - if (te.th32OwnerProcessID == k->pid) { - ret |= PostThreadMessage(te.th32ThreadID, WM_QUIT, k->exitcode, 0); - } - } - - CloseHandle(snapshot); - - return ret; -} - -int kill_threads(kill_t *k) { - return kill_threads(NULL, k); -} - -/* Give the process a chance to die gracefully. */ -int kill_process(nssm_service_t *service, kill_t *k) { - if (! k) return 1; - - unsigned long ret; - if (GetExitCodeProcess(k->process_handle, &ret)) { - if (ret != STILL_ACTIVE) return 1; - } - - /* Try to send a Control-C event to the console. */ - if (k->stop_method & NSSM_STOP_METHOD_CONSOLE) { - if (! kill_console(k)) return 1; - } - - /* - Try to post messages to the windows belonging to the given process ID. - If the process is a console application it won't have any windows so there's - no guarantee of success. - */ - if (k->stop_method & NSSM_STOP_METHOD_WINDOW) { - EnumWindows((WNDENUMPROC) kill_window, (LPARAM) k); - if (k->signalled) { - if (! await_single_handle(k->status_handle, k->status, k->process_handle, k->name, _T(__FUNCTION__), k->kill_window_delay)) return 1; - k->signalled = 0; - } - } - - /* - Try to post messages to any thread message queues associated with the - process. Console applications might have them (but probably won't) so - there's still no guarantee of success. - */ - if (k->stop_method & NSSM_STOP_METHOD_THREADS) { - if (kill_threads(k)) { - if (! await_single_handle(k->status_handle, k->status, k->process_handle, k->name, _T(__FUNCTION__), k->kill_threads_delay)) return 1; - } - } - - /* We tried being nice. Time for extreme prejudice. */ - if (k->stop_method & NSSM_STOP_METHOD_TERMINATE) { - return TerminateProcess(k->process_handle, k->exitcode); - } - - return 0; -} - -int kill_process(kill_t *k) { - return kill_process(NULL, k); -} - -/* Simulate a Control-C event to our console (shared with the app). */ -int kill_console(nssm_service_t *service, kill_t *k) { - unsigned long ret; - - if (! k) return 1; - - /* Check we loaded AttachConsole(). */ - if (! imports.AttachConsole) return 4; - - /* Try to attach to the process's console. */ - if (! imports.AttachConsole(k->pid)) { - ret = GetLastError(); - - switch (ret) { - case ERROR_INVALID_HANDLE: - /* The app doesn't have a console. */ - return 1; - - case ERROR_GEN_FAILURE: - /* The app already exited. */ - return 2; - - case ERROR_ACCESS_DENIED: - default: - /* We already have a console. */ - log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_ATTACHCONSOLE_FAILED, k->name, error_string(ret), 0); - return 3; - } - } - - /* Ignore the event ourselves. */ - ret = 0; - if (! SetConsoleCtrlHandler(0, TRUE)) { - log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETCONSOLECTRLHANDLER_FAILED, k->name, error_string(GetLastError()), 0); - ret = 4; - } - - /* Send the event. */ - if (! ret) { - if (! GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0)) { - log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GENERATECONSOLECTRLEVENT_FAILED, k->name, error_string(GetLastError()), 0); - ret = 5; - } - } - - /* Detach from the console. */ - if (! FreeConsole()) { - log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_FREECONSOLE_FAILED, k->name, error_string(GetLastError()), 0); - } - - /* Wait for process to exit. */ - if (await_single_handle(k->status_handle, k->status, k->process_handle, k->name, _T(__FUNCTION__), k->kill_console_delay)) ret = 6; - - return ret; -} - -int kill_console(kill_t *k) { - return kill_console(NULL, k); -} - -void kill_process_tree(nssm_service_t * service, kill_t *k, unsigned long ppid) { - if (! k) return; - /* Shouldn't happen unless the service failed to start. */ - if (! k->pid) return; /* XXX: needed? */ - unsigned long pid = k->pid; - - TCHAR pid_string[16], code[16]; - _sntprintf_s(pid_string, _countof(pid_string), _TRUNCATE, _T("%lu"), pid); - _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), k->exitcode); - log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_KILLING, k->name, pid_string, code, 0); - - /* We will need a process handle in order to call TerminateProcess() later. */ - HANDLE process_handle = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_TERMINATE, false, pid); - if (process_handle) { - /* Kill this process first, then its descendents. */ - TCHAR ppid_string[16]; - _sntprintf_s(ppid_string, _countof(ppid_string), _TRUNCATE, _T("%lu"), ppid); - log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_KILL_PROCESS_TREE, pid_string, ppid_string, k->name, 0); - k->process_handle = process_handle; /* XXX: open directly? */ - if (! kill_process(k)) { - /* Maybe it already died. */ - unsigned long ret; - if (! GetExitCodeProcess(process_handle, &ret) || ret == STILL_ACTIVE) { - if (k->stop_method & NSSM_STOP_METHOD_TERMINATE) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_TERMINATEPROCESS_FAILED, pid_string, k->name, error_string(GetLastError()), 0); - else log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_PROCESS_STILL_ACTIVE, k->name, pid_string, NSSM, NSSM_REG_STOP_METHOD_SKIP, 0); - } - } - - CloseHandle(process_handle); - } - else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENPROCESS_FAILED, pid_string, k->name, error_string(GetLastError()), 0); - - /* Get a snapshot of all processes in the system. */ - HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); - if (! snapshot) { - log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETOOLHELP32SNAPSHOT_PROCESS_FAILED, k->name, error_string(GetLastError()), 0); - return; - } - - PROCESSENTRY32 pe; - ZeroMemory(&pe, sizeof(pe)); - pe.dwSize = sizeof(pe); - - if (! Process32First(snapshot, &pe)) { - log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_PROCESS_ENUMERATE_FAILED, k->name, error_string(GetLastError()), 0); - CloseHandle(snapshot); - return; - } - - /* This is a child of the doomed process so kill it. */ - if (! check_parent(k, &pe, pid)) { - k->pid = pe.th32ProcessID; - kill_process_tree(k, ppid); - } - k->pid = pid; - - while (true) { - /* Try to get the next process. */ - if (! Process32Next(snapshot, &pe)) { - unsigned long ret = GetLastError(); - if (ret == ERROR_NO_MORE_FILES) break; - log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_PROCESS_ENUMERATE_FAILED, k->name, error_string(GetLastError()), 0); - CloseHandle(snapshot); - return; - } - - if (! check_parent(k, &pe, pid)) { - k->pid = pe.th32ProcessID; - kill_process_tree(k, ppid); - } - k->pid = pid; - } - - CloseHandle(snapshot); -} - -void kill_process_tree(kill_t *k, unsigned long ppid) { - return kill_process_tree(NULL, k, ppid); -} +#include "nssm.h" + +extern imports_t imports; + +void service_kill_t(nssm_service_t *service, kill_t *k) { + if (! service) return; + if (! k) return; + + ZeroMemory(k, sizeof(*k)); + k->name = service->name; + k->process_handle = service->process_handle; + k->pid = service->pid; + k->exitcode = service->exitcode; + k->stop_method = service->stop_method; + k->kill_console_delay = service->kill_console_delay; + k->kill_window_delay = service->kill_window_delay; + k->kill_threads_delay = service->kill_threads_delay; + k->status_handle = service->status_handle; + k->status = &service->status; + k->creation_time = service->creation_time; + k->exit_time = service->exit_time; +} + +int get_process_creation_time(HANDLE process_handle, FILETIME *ft) { + FILETIME creation_time, exit_time, kernel_time, user_time; + + if (! GetProcessTimes(process_handle, &creation_time, &exit_time, &kernel_time, &user_time)) { + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GETPROCESSTIMES_FAILED, error_string(GetLastError()), 0); + return 1; + } + + memmove(ft, &creation_time, sizeof(creation_time)); + + return 0; +} + +int get_process_exit_time(HANDLE process_handle, FILETIME *ft) { + FILETIME creation_time, exit_time, kernel_time, user_time; + + if (! GetProcessTimes(process_handle, &creation_time, &exit_time, &kernel_time, &user_time)) { + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GETPROCESSTIMES_FAILED, error_string(GetLastError()), 0); + return 1; + } + + if (! (exit_time.dwLowDateTime || exit_time.dwHighDateTime)) return 2; + memmove(ft, &exit_time, sizeof(exit_time)); + + return 0; +} + +int check_parent(kill_t *k, PROCESSENTRY32 *pe, unsigned long ppid) { + /* Check parent process ID matches. */ + if (pe->th32ParentProcessID != ppid) return 1; + + /* + Process IDs can be reused so do a sanity check by making sure the child + has been running for less time than the parent. + Though unlikely, it's possible that the parent exited and its process ID + was already reused, so we'll also compare against its exit time. + */ + HANDLE process_handle = OpenProcess(PROCESS_QUERY_INFORMATION, false, pe->th32ProcessID); + if (! process_handle) { + TCHAR pid_string[16]; + _sntprintf_s(pid_string, _countof(pid_string), _TRUNCATE, _T("%lu"), pe->th32ProcessID); + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENPROCESS_FAILED, pid_string, k->name, error_string(GetLastError()), 0); + return 2; + } + + FILETIME ft; + if (get_process_creation_time(process_handle, &ft)) { + CloseHandle(process_handle); + return 3; + } + + CloseHandle(process_handle); + + /* Verify that the parent's creation time is not later. */ + if (CompareFileTime(&k->creation_time, &ft) > 0) return 4; + + /* Verify that the parent's exit time is not earlier. */ + if (CompareFileTime(&k->exit_time, &ft) < 0) return 5; + + return 0; +} + +/* Send some window messages and hope the window respects one or more. */ +int CALLBACK kill_window(HWND window, LPARAM arg) { + kill_t *k = (kill_t *) arg; + + unsigned long pid; + if (! GetWindowThreadProcessId(window, &pid)) return 1; + if (pid != k->pid) return 1; + + /* First try sending WM_CLOSE to request that the window close. */ + k->signalled |= PostMessage(window, WM_CLOSE, k->exitcode, 0); + + /* + Then tell the window that the user is logging off and it should exit + without worrying about saving any data. + */ + k->signalled |= PostMessage(window, WM_ENDSESSION, 1, ENDSESSION_CLOSEAPP | ENDSESSION_CRITICAL | ENDSESSION_LOGOFF); + + return 1; +} + +/* + Try to post a message to the message queues of threads associated with the + given process ID. Not all threads have message queues so there's no + guarantee of success, and we don't want to be left waiting for unsignalled + processes so this function returns only true if at least one thread was + successfully prodded. +*/ +int kill_threads(nssm_service_t *service, kill_t *k) { + int ret = 0; + + /* Get a snapshot of all threads in the system. */ + HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); + if (! snapshot) { + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETOOLHELP32SNAPSHOT_THREAD_FAILED, k->name, error_string(GetLastError()), 0); + return 0; + } + + THREADENTRY32 te; + ZeroMemory(&te, sizeof(te)); + te.dwSize = sizeof(te); + + if (! Thread32First(snapshot, &te)) { + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_THREAD_ENUMERATE_FAILED, k->name, error_string(GetLastError()), 0); + CloseHandle(snapshot); + return 0; + } + + /* This thread belongs to the doomed process so signal it. */ + if (te.th32OwnerProcessID == k->pid) { + ret |= PostThreadMessage(te.th32ThreadID, WM_QUIT, k->exitcode, 0); + } + + while (true) { + /* Try to get the next thread. */ + if (! Thread32Next(snapshot, &te)) { + unsigned long error = GetLastError(); + if (error == ERROR_NO_MORE_FILES) break; + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_THREAD_ENUMERATE_FAILED, k->name, error_string(GetLastError()), 0); + CloseHandle(snapshot); + return ret; + } + + if (te.th32OwnerProcessID == k->pid) { + ret |= PostThreadMessage(te.th32ThreadID, WM_QUIT, k->exitcode, 0); + } + } + + CloseHandle(snapshot); + + return ret; +} + +int kill_threads(kill_t *k) { + return kill_threads(NULL, k); +} + +/* Give the process a chance to die gracefully. */ +int kill_process(nssm_service_t *service, kill_t *k) { + if (! k) return 1; + + unsigned long ret; + if (GetExitCodeProcess(k->process_handle, &ret)) { + if (ret != STILL_ACTIVE) return 1; + } + + /* Try to send a Control-C event to the console. */ + if (k->stop_method & NSSM_STOP_METHOD_CONSOLE) { + if (! kill_console(k)) return 1; + } + + /* + Try to post messages to the windows belonging to the given process ID. + If the process is a console application it won't have any windows so there's + no guarantee of success. + */ + if (k->stop_method & NSSM_STOP_METHOD_WINDOW) { + EnumWindows((WNDENUMPROC) kill_window, (LPARAM) k); + if (k->signalled) { + if (! await_single_handle(k->status_handle, k->status, k->process_handle, k->name, _T(__FUNCTION__), k->kill_window_delay)) return 1; + k->signalled = 0; + } + } + + /* + Try to post messages to any thread message queues associated with the + process. Console applications might have them (but probably won't) so + there's still no guarantee of success. + */ + if (k->stop_method & NSSM_STOP_METHOD_THREADS) { + if (kill_threads(k)) { + if (! await_single_handle(k->status_handle, k->status, k->process_handle, k->name, _T(__FUNCTION__), k->kill_threads_delay)) return 1; + } + } + + /* We tried being nice. Time for extreme prejudice. */ + if (k->stop_method & NSSM_STOP_METHOD_TERMINATE) { + return TerminateProcess(k->process_handle, k->exitcode); + } + + return 0; +} + +int kill_process(kill_t *k) { + return kill_process(NULL, k); +} + +/* Simulate a Control-C event to our console (shared with the app). */ +int kill_console(nssm_service_t *service, kill_t *k) { + unsigned long ret; + + if (! k) return 1; + + /* Check we loaded AttachConsole(). */ + if (! imports.AttachConsole) return 4; + + /* Try to attach to the process's console. */ + if (! imports.AttachConsole(k->pid)) { + ret = GetLastError(); + + switch (ret) { + case ERROR_INVALID_HANDLE: + /* The app doesn't have a console. */ + return 1; + + case ERROR_GEN_FAILURE: + /* The app already exited. */ + return 2; + + case ERROR_ACCESS_DENIED: + default: + /* We already have a console. */ + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_ATTACHCONSOLE_FAILED, k->name, error_string(ret), 0); + return 3; + } + } + + /* Ignore the event ourselves. */ + ret = 0; + if (! SetConsoleCtrlHandler(0, TRUE)) { + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETCONSOLECTRLHANDLER_FAILED, k->name, error_string(GetLastError()), 0); + ret = 4; + } + + /* Send the event. */ + if (! ret) { + if (! GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0)) { + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GENERATECONSOLECTRLEVENT_FAILED, k->name, error_string(GetLastError()), 0); + ret = 5; + } + } + + /* Detach from the console. */ + if (! FreeConsole()) { + log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_FREECONSOLE_FAILED, k->name, error_string(GetLastError()), 0); + } + + /* Wait for process to exit. */ + if (await_single_handle(k->status_handle, k->status, k->process_handle, k->name, _T(__FUNCTION__), k->kill_console_delay)) ret = 6; + + return ret; +} + +int kill_console(kill_t *k) { + return kill_console(NULL, k); +} + +void kill_process_tree(nssm_service_t * service, kill_t *k, unsigned long ppid) { + if (! k) return; + /* Shouldn't happen unless the service failed to start. */ + if (! k->pid) return; /* XXX: needed? */ + unsigned long pid = k->pid; + + TCHAR pid_string[16], code[16]; + _sntprintf_s(pid_string, _countof(pid_string), _TRUNCATE, _T("%lu"), pid); + _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), k->exitcode); + log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_KILLING, k->name, pid_string, code, 0); + + /* We will need a process handle in order to call TerminateProcess() later. */ + HANDLE process_handle = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_TERMINATE, false, pid); + if (process_handle) { + /* Kill this process first, then its descendents. */ + TCHAR ppid_string[16]; + _sntprintf_s(ppid_string, _countof(ppid_string), _TRUNCATE, _T("%lu"), ppid); + log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_KILL_PROCESS_TREE, pid_string, ppid_string, k->name, 0); + k->process_handle = process_handle; /* XXX: open directly? */ + if (! kill_process(k)) { + /* Maybe it already died. */ + unsigned long ret; + if (! GetExitCodeProcess(process_handle, &ret) || ret == STILL_ACTIVE) { + if (k->stop_method & NSSM_STOP_METHOD_TERMINATE) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_TERMINATEPROCESS_FAILED, pid_string, k->name, error_string(GetLastError()), 0); + else log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_PROCESS_STILL_ACTIVE, k->name, pid_string, NSSM, NSSM_REG_STOP_METHOD_SKIP, 0); + } + } + + CloseHandle(process_handle); + } + else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENPROCESS_FAILED, pid_string, k->name, error_string(GetLastError()), 0); + + /* Get a snapshot of all processes in the system. */ + HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (! snapshot) { + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETOOLHELP32SNAPSHOT_PROCESS_FAILED, k->name, error_string(GetLastError()), 0); + return; + } + + PROCESSENTRY32 pe; + ZeroMemory(&pe, sizeof(pe)); + pe.dwSize = sizeof(pe); + + if (! Process32First(snapshot, &pe)) { + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_PROCESS_ENUMERATE_FAILED, k->name, error_string(GetLastError()), 0); + CloseHandle(snapshot); + return; + } + + /* This is a child of the doomed process so kill it. */ + if (! check_parent(k, &pe, pid)) { + k->pid = pe.th32ProcessID; + kill_process_tree(k, ppid); + } + k->pid = pid; + + while (true) { + /* Try to get the next process. */ + if (! Process32Next(snapshot, &pe)) { + unsigned long ret = GetLastError(); + if (ret == ERROR_NO_MORE_FILES) break; + log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_PROCESS_ENUMERATE_FAILED, k->name, error_string(GetLastError()), 0); + CloseHandle(snapshot); + return; + } + + if (! check_parent(k, &pe, pid)) { + k->pid = pe.th32ProcessID; + kill_process_tree(k, ppid); + } + k->pid = pid; + } + + CloseHandle(snapshot); +} + +void kill_process_tree(kill_t *k, unsigned long ppid) { + return kill_process_tree(NULL, k, ppid); +} diff --git a/process.h b/process.h index 2e402e4..1bfc273 100644 --- a/process.h +++ b/process.h @@ -1,36 +1,36 @@ -#ifndef PROCESS_H -#define PROCESS_H - -#include - -typedef struct { - TCHAR *name; - HANDLE process_handle; - unsigned long pid; - unsigned long exitcode; - unsigned long stop_method; - unsigned long kill_console_delay; - unsigned long kill_window_delay; - unsigned long kill_threads_delay; - SERVICE_STATUS_HANDLE status_handle; - SERVICE_STATUS *status; - FILETIME creation_time; - FILETIME exit_time; - int signalled; -} kill_t; - -void service_kill_t(nssm_service_t *, kill_t *); -int get_process_creation_time(HANDLE, FILETIME *); -int get_process_exit_time(HANDLE, FILETIME *); -int check_parent(kill_t *, PROCESSENTRY32 *, unsigned long); -int CALLBACK kill_window(HWND, LPARAM); -int kill_threads(nssm_service_t *, kill_t *); -int kill_threads(kill_t *); -int kill_console(nssm_service_t *, kill_t *); -int kill_console(kill_t *); -int kill_process(nssm_service_t *, kill_t *); -int kill_process(kill_t *); -void kill_process_tree(nssm_service_t *, kill_t *, unsigned long); -void kill_process_tree(kill_t *, unsigned long); - -#endif +#ifndef PROCESS_H +#define PROCESS_H + +#include + +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 diff --git a/settings.cpp b/settings.cpp index 4bbdd57..45e0a58 100644 --- a/settings.cpp +++ b/settings.cpp @@ -1,1130 +1,1130 @@ -#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 = NULL NULL */ - if (! _tcsnicmp(s, additional, len) && s[len] == _T('=')) { - /* Strip = */ - 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 ObjectName - 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" +/* 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 = NULL NULL */ + if (! _tcsnicmp(s, additional, len) && s[len] == _T('=')) { + /* Strip = */ + 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 ObjectName + 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 } +}; diff --git a/settings.h b/settings.h index 2fcffa5..c28df38 100644 --- a/settings.h +++ b/settings.h @@ -1,48 +1,48 @@ -#ifndef SETTINGS_H -#define SETTINGS_H - -#define NSSM_NATIVE_DEPENDONGROUP _T("DependOnGroup") -#define NSSM_NATIVE_DEPENDONSERVICE _T("DependOnService") -#define NSSM_NATIVE_DESCRIPTION _T("Description") -#define NSSM_NATIVE_DISPLAYNAME _T("DisplayName") -#define NSSM_NATIVE_ENVIRONMENT _T("Environment") -#define NSSM_NATIVE_IMAGEPATH _T("ImagePath") -#define NSSM_NATIVE_NAME _T("Name") -#define NSSM_NATIVE_OBJECTNAME _T("ObjectName") -#define NSSM_NATIVE_STARTUP _T("Start") -#define NSSM_NATIVE_TYPE _T("Type") - -/* Are additional arguments needed? */ -#define ADDITIONAL_GETTING (1 << 0) -#define ADDITIONAL_SETTING (1 << 1) -#define ADDITIONAL_RESETTING (1 << 2) -#define ADDITIONAL_CRLF (1 << 3) -#define ADDITIONAL_MANDATORY ADDITIONAL_GETTING|ADDITIONAL_SETTING|ADDITIONAL_RESETTING - -#define DEPENDENCY_SERVICES (1 << 0) -#define DEPENDENCY_GROUPS (1 << 1) -#define DEPENDENCY_ALL (DEPENDENCY_SERVICES|DEPENDENCY_GROUPS) - -typedef union { - unsigned long numeric; - TCHAR *string; -} value_t; - -typedef int (*setting_function_t)(const TCHAR *, void *, const TCHAR *, void *, value_t *, const TCHAR *); - -typedef struct { - const TCHAR *name; - unsigned long type; - void *default_value; - bool native; - int additional; - setting_function_t set; - setting_function_t get; -} settings_t; - -int set_setting(const TCHAR *, HKEY, settings_t *, value_t *, const TCHAR *); -int set_setting(const TCHAR *, SC_HANDLE, settings_t *, value_t *, const TCHAR *); -int get_setting(const TCHAR *, HKEY, settings_t *, value_t *, const TCHAR *); -int get_setting(const TCHAR *, SC_HANDLE, settings_t *, value_t *, const TCHAR *); - -#endif +#ifndef SETTINGS_H +#define SETTINGS_H + +#define NSSM_NATIVE_DEPENDONGROUP _T("DependOnGroup") +#define NSSM_NATIVE_DEPENDONSERVICE _T("DependOnService") +#define NSSM_NATIVE_DESCRIPTION _T("Description") +#define NSSM_NATIVE_DISPLAYNAME _T("DisplayName") +#define NSSM_NATIVE_ENVIRONMENT _T("Environment") +#define NSSM_NATIVE_IMAGEPATH _T("ImagePath") +#define NSSM_NATIVE_NAME _T("Name") +#define NSSM_NATIVE_OBJECTNAME _T("ObjectName") +#define NSSM_NATIVE_STARTUP _T("Start") +#define NSSM_NATIVE_TYPE _T("Type") + +/* Are additional arguments needed? */ +#define ADDITIONAL_GETTING (1 << 0) +#define ADDITIONAL_SETTING (1 << 1) +#define ADDITIONAL_RESETTING (1 << 2) +#define ADDITIONAL_CRLF (1 << 3) +#define ADDITIONAL_MANDATORY ADDITIONAL_GETTING|ADDITIONAL_SETTING|ADDITIONAL_RESETTING + +#define DEPENDENCY_SERVICES (1 << 0) +#define DEPENDENCY_GROUPS (1 << 1) +#define DEPENDENCY_ALL (DEPENDENCY_SERVICES|DEPENDENCY_GROUPS) + +typedef union { + unsigned long numeric; + TCHAR *string; +} value_t; + +typedef int (*setting_function_t)(const TCHAR *, void *, const TCHAR *, void *, value_t *, const TCHAR *); + +typedef struct { + const TCHAR *name; + unsigned long type; + void *default_value; + bool native; + int additional; + setting_function_t set; + setting_function_t get; +} settings_t; + +int set_setting(const TCHAR *, HKEY, settings_t *, value_t *, const TCHAR *); +int set_setting(const TCHAR *, SC_HANDLE, settings_t *, value_t *, const TCHAR *); +int get_setting(const TCHAR *, HKEY, settings_t *, value_t *, const TCHAR *); +int get_setting(const TCHAR *, SC_HANDLE, settings_t *, value_t *, const TCHAR *); + +#endif