service.cpp 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213
  1. #include "nssm.h"
  2. /* This is explicitly a wide string. */
  3. #define NSSM_LOGON_AS_SERVICE_RIGHT L"SeServiceLogonRight"
  4. extern const TCHAR *exit_action_strings[];
  5. bool is_admin;
  6. bool use_critical_section;
  7. extern imports_t imports;
  8. const TCHAR *exit_action_strings[] = { _T("Restart"), _T("Ignore"), _T("Exit"), _T("Suicide"), 0 };
  9. static inline int throttle_milliseconds(unsigned long throttle) {
  10. /* pow() operates on doubles. */
  11. int ret = 1; for (unsigned long i = 1; i < throttle; i++) ret *= 2;
  12. return ret * 1000;
  13. }
  14. /*
  15. Wrapper to be called in a new thread so that we can acknowledge a STOP
  16. control immediately.
  17. */
  18. static unsigned long WINAPI shutdown_service(void *arg) {
  19. return stop_service((nssm_service_t *) arg, 0, true, true);
  20. }
  21. /* Connect to the service manager */
  22. SC_HANDLE open_service_manager() {
  23. SC_HANDLE ret = OpenSCManager(0, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS);
  24. if (! ret) {
  25. if (is_admin) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENSCMANAGER_FAILED, 0);
  26. return 0;
  27. }
  28. return ret;
  29. }
  30. QUERY_SERVICE_CONFIG *query_service_config(const TCHAR *service_name, SC_HANDLE service_handle) {
  31. QUERY_SERVICE_CONFIG *qsc;
  32. unsigned long bufsize;
  33. unsigned long error;
  34. QueryServiceConfig(service_handle, 0, 0, &bufsize);
  35. error = GetLastError();
  36. if (error == ERROR_INSUFFICIENT_BUFFER) {
  37. qsc = (QUERY_SERVICE_CONFIG *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, bufsize);
  38. if (! qsc) {
  39. print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("QUERY_SERVICE_CONFIG"), _T("query_service_config()"), 0);
  40. return 0;
  41. }
  42. }
  43. else {
  44. print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG_FAILED, service_name, error_string(error), 0);
  45. return 0;
  46. }
  47. if (! QueryServiceConfig(service_handle, qsc, bufsize, &bufsize)) {
  48. HeapFree(GetProcessHeap(), 0, qsc);
  49. print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG_FAILED, service_name, error_string(GetLastError()), 0);
  50. return 0;
  51. }
  52. return qsc;
  53. }
  54. int get_service_description(const TCHAR *service_name, SC_HANDLE service_handle, unsigned long len, TCHAR *buffer) {
  55. if (! buffer) return 1;
  56. unsigned long bufsize;
  57. QueryServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, 0, 0, &bufsize);
  58. unsigned long error = GetLastError();
  59. if (error == ERROR_INSUFFICIENT_BUFFER) {
  60. SERVICE_DESCRIPTION *description = (SERVICE_DESCRIPTION *) HeapAlloc(GetProcessHeap(), 0, bufsize);
  61. if (! description) {
  62. print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SERVICE_CONFIG_DESCRIPTION"), _T("get_service_description()"));
  63. return 2;
  64. }
  65. if (QueryServiceConfig2(service_handle, SERVICE_CONFIG_DESCRIPTION, (unsigned char *) description, bufsize, &bufsize)) {
  66. if (description->lpDescription) _sntprintf_s(buffer, len, _TRUNCATE, _T("%s"), description->lpDescription);
  67. else ZeroMemory(buffer, len * sizeof(TCHAR));
  68. HeapFree(GetProcessHeap(), 0, description);
  69. return 0;
  70. }
  71. else {
  72. HeapFree(GetProcessHeap(), 0, description);
  73. print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DESCRIPTION"), error_string(error));
  74. return 3;
  75. }
  76. }
  77. else {
  78. print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DESCRIPTION"), error_string(error));
  79. return 4;
  80. }
  81. return 0;
  82. }
  83. int get_service_startup(const TCHAR *service_name, SC_HANDLE service_handle, const QUERY_SERVICE_CONFIG *qsc, unsigned long *startup) {
  84. if (! qsc) return 1;
  85. switch (qsc->dwStartType) {
  86. case SERVICE_DEMAND_START: *startup = NSSM_STARTUP_MANUAL; break;
  87. case SERVICE_DISABLED: *startup = NSSM_STARTUP_DISABLED; break;
  88. default: *startup = NSSM_STARTUP_AUTOMATIC;
  89. }
  90. if (*startup != NSSM_STARTUP_AUTOMATIC) return 0;
  91. /* Check for delayed start. */
  92. unsigned long bufsize;
  93. unsigned long error;
  94. QueryServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, 0, 0, &bufsize);
  95. error = GetLastError();
  96. if (error == ERROR_INSUFFICIENT_BUFFER) {
  97. SERVICE_DELAYED_AUTO_START_INFO *info = (SERVICE_DELAYED_AUTO_START_INFO *) HeapAlloc(GetProcessHeap(), 0, bufsize);
  98. if (! info) {
  99. print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SERVICE_DELAYED_AUTO_START_INFO"), _T("get_service_startup()"));
  100. return 2;
  101. }
  102. if (QueryServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, (unsigned char *) info, bufsize, &bufsize)) {
  103. if (info->fDelayedAutostart) *startup = NSSM_STARTUP_DELAYED;
  104. HeapFree(GetProcessHeap(), 0, info);
  105. return 0;
  106. }
  107. else {
  108. error = GetLastError();
  109. if (error != ERROR_INVALID_LEVEL) {
  110. print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_CONFIG_DELAYED_AUTO_START_INFO"), error_string(error));
  111. return 3;
  112. }
  113. }
  114. }
  115. else if (error != ERROR_INVALID_LEVEL) {
  116. print_message(stderr, NSSM_MESSAGE_QUERYSERVICECONFIG2_FAILED, service_name, _T("SERVICE_DELAYED_AUTO_START_INFO"), error_string(error));
  117. return 3;
  118. }
  119. return 0;
  120. }
  121. int get_service_username(const TCHAR *service_name, const QUERY_SERVICE_CONFIG *qsc, TCHAR **username, size_t *usernamelen) {
  122. if (! username) return 1;
  123. if (! usernamelen) return 1;
  124. *username = 0;
  125. *usernamelen = 0;
  126. if (! qsc) return 1;
  127. if (str_equiv(qsc->lpServiceStartName, NSSM_LOCALSYSTEM_ACCOUNT)) return 0;
  128. size_t len = _tcslen(qsc->lpServiceStartName);
  129. *username = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(TCHAR));
  130. if (! *username) {
  131. print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("username"), _T("get_service_username()"));
  132. return 2;
  133. }
  134. memmove(*username, qsc->lpServiceStartName, (len + 1) * sizeof(TCHAR));
  135. *usernamelen = len;
  136. return 0;
  137. }
  138. static int grant_logon_as_service(const TCHAR *username) {
  139. if (str_equiv(username, NSSM_LOCALSYSTEM_ACCOUNT)) return 0;
  140. /* Open Policy object. */
  141. LSA_OBJECT_ATTRIBUTES attributes;
  142. ZeroMemory(&attributes, sizeof(attributes));
  143. LSA_HANDLE policy;
  144. NTSTATUS status = LsaOpenPolicy(0, &attributes, POLICY_ALL_ACCESS, &policy);
  145. if (status) {
  146. print_message(stderr, NSSM_MESSAGE_LSAOPENPOLICY_FAILED, error_string(LsaNtStatusToWinError(status)));
  147. return 1;
  148. }
  149. /* Look up SID for the account. */
  150. LSA_UNICODE_STRING lsa_username;
  151. #ifdef UNICODE
  152. lsa_username.Buffer = (wchar_t *) username;
  153. lsa_username.Length = (unsigned short) _tcslen(username) * sizeof(TCHAR);
  154. lsa_username.MaximumLength = lsa_username.Length + sizeof(TCHAR);
  155. #else
  156. size_t buflen;
  157. mbstowcs_s(&buflen, NULL, 0, username, _TRUNCATE);
  158. lsa_username.MaximumLength = buflen * sizeof(wchar_t);
  159. lsa_username.Length = lsa_username.MaximumLength - sizeof(wchar_t);
  160. lsa_username.Buffer = (wchar_t *) HeapAlloc(GetProcessHeap(), 0, lsa_username.MaximumLength);
  161. if (lsa_username.Buffer) mbstowcs_s(&buflen, lsa_username.Buffer, lsa_username.MaximumLength, username, _TRUNCATE);
  162. else {
  163. LsaClose(policy);
  164. print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("LSA_UNICODE_STRING"), _T("grant_logon_as_service()"));
  165. return 2;
  166. }
  167. #endif
  168. LSA_REFERENCED_DOMAIN_LIST *translated_domains;
  169. LSA_TRANSLATED_SID *translated_sid;
  170. status = LsaLookupNames(policy, 1, &lsa_username, &translated_domains, &translated_sid);
  171. #ifndef UNICODE
  172. HeapFree(GetProcessHeap(), 0, lsa_username.Buffer);
  173. #endif
  174. if (status) {
  175. LsaFreeMemory(translated_domains);
  176. LsaFreeMemory(translated_sid);
  177. LsaClose(policy);
  178. print_message(stderr, NSSM_MESSAGE_LSALOOKUPNAMES_FAILED, username, error_string(LsaNtStatusToWinError(status)));
  179. return 3;
  180. }
  181. if (translated_sid->Use != SidTypeUser) {
  182. LsaFreeMemory(translated_domains);
  183. LsaFreeMemory(translated_sid);
  184. LsaClose(policy);
  185. print_message(stderr, NSSM_GUI_INVALID_USERNAME, username);
  186. return 4;
  187. }
  188. LSA_TRUST_INFORMATION *trust = &translated_domains->Domains[translated_sid->DomainIndex];
  189. if (! trust || ! IsValidSid(trust->Sid)) {
  190. LsaFreeMemory(translated_domains);
  191. LsaFreeMemory(translated_sid);
  192. LsaClose(policy);
  193. print_message(stderr, NSSM_GUI_INVALID_USERNAME, username);
  194. return 4;
  195. }
  196. /* GetSidSubAuthority*() return pointers! */
  197. unsigned char *n = GetSidSubAuthorityCount(trust->Sid);
  198. /* Convert translated SID to SID. */
  199. SID *sid = (SID *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, GetSidLengthRequired(*n + 1));
  200. if (! sid) {
  201. LsaFreeMemory(translated_domains);
  202. LsaFreeMemory(translated_sid);
  203. LsaClose(policy);
  204. print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("SID"), _T("grant_logon_as_service"));
  205. return 4;
  206. }
  207. unsigned long error;
  208. if (! InitializeSid(sid, GetSidIdentifierAuthority(trust->Sid), *n + 1)) {
  209. error = GetLastError();
  210. HeapFree(GetProcessHeap(), 0, sid);
  211. LsaFreeMemory(translated_domains);
  212. LsaFreeMemory(translated_sid);
  213. LsaClose(policy);
  214. print_message(stderr, NSSM_MESSAGE_INITIALIZESID_FAILED, username, error_string(error));
  215. return 5;
  216. }
  217. for (unsigned char i = 0; i <= *n; i++) {
  218. unsigned long *sub = GetSidSubAuthority(sid, i);
  219. if (i < *n) *sub = *GetSidSubAuthority(trust->Sid, i);
  220. else *sub = translated_sid->RelativeId;
  221. }
  222. LsaFreeMemory(translated_domains);
  223. LsaFreeMemory(translated_sid);
  224. /* Check if the SID has the "Log on as a service" right. */
  225. LSA_UNICODE_STRING lsa_right;
  226. lsa_right.Buffer = NSSM_LOGON_AS_SERVICE_RIGHT;
  227. lsa_right.Length = (unsigned short) wcslen(lsa_right.Buffer) * sizeof(wchar_t);
  228. lsa_right.MaximumLength = lsa_right.Length + sizeof(wchar_t);
  229. LSA_UNICODE_STRING *rights;
  230. unsigned long count = ~0;
  231. status = LsaEnumerateAccountRights(policy, sid, &rights, &count);
  232. if (status) {
  233. /*
  234. If the account has no rights set LsaEnumerateAccountRights() will return
  235. STATUS_OBJECT_NAME_NOT_FOUND and set count to 0.
  236. */
  237. error = LsaNtStatusToWinError(status);
  238. if (error != ERROR_FILE_NOT_FOUND) {
  239. HeapFree(GetProcessHeap(), 0, sid);
  240. LsaClose(policy);
  241. print_message(stderr, NSSM_MESSAGE_LSAENUMERATEACCOUNTRIGHTS_FAILED, username, error_string(error));
  242. return 4;
  243. }
  244. }
  245. for (unsigned long i = 0; i < count; i++) {
  246. if (rights[i].Length != lsa_right.Length) continue;
  247. if (_wcsnicmp(rights[i].Buffer, lsa_right.Buffer, lsa_right.MaximumLength)) continue;
  248. /* The SID has the right. */
  249. HeapFree(GetProcessHeap(), 0, sid);
  250. LsaFreeMemory(rights);
  251. LsaClose(policy);
  252. return 0;
  253. }
  254. LsaFreeMemory(rights);
  255. /* Add the right. */
  256. status = LsaAddAccountRights(policy, sid, &lsa_right, 1);
  257. HeapFree(GetProcessHeap(), 0, sid);
  258. LsaClose(policy);
  259. if (status) {
  260. print_message(stderr, NSSM_MESSAGE_LSAADDACCOUNTRIGHTS_FAILED, error_string(LsaNtStatusToWinError(status)));
  261. return 5;
  262. }
  263. print_message(stdout, NSSM_MESSAGE_GRANTED_LOGON_AS_SERVICE, username);
  264. return 0;
  265. }
  266. /* Set default values which aren't zero. */
  267. void set_nssm_service_defaults(nssm_service_t *service) {
  268. if (! service) return;
  269. service->type = SERVICE_WIN32_OWN_PROCESS;
  270. service->stdin_sharing = NSSM_STDIN_SHARING;
  271. service->stdin_disposition = NSSM_STDIN_DISPOSITION;
  272. service->stdin_flags = NSSM_STDIN_FLAGS;
  273. service->stdout_sharing = NSSM_STDOUT_SHARING;
  274. service->stdout_disposition = NSSM_STDOUT_DISPOSITION;
  275. service->stdout_flags = NSSM_STDOUT_FLAGS;
  276. service->stderr_sharing = NSSM_STDERR_SHARING;
  277. service->stderr_disposition = NSSM_STDERR_DISPOSITION;
  278. service->stderr_flags = NSSM_STDERR_FLAGS;
  279. service->throttle_delay = NSSM_RESET_THROTTLE_RESTART;
  280. service->stop_method = ~0;
  281. service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
  282. service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
  283. service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
  284. }
  285. /* Allocate and zero memory for a service. */
  286. nssm_service_t *alloc_nssm_service() {
  287. nssm_service_t *service = (nssm_service_t *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(nssm_service_t));
  288. if (! service) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service"), _T("alloc_nssm_service()"), 0);
  289. return service;
  290. }
  291. /* Free memory for a service. */
  292. void cleanup_nssm_service(nssm_service_t *service) {
  293. if (! service) return;
  294. if (service->username) HeapFree(GetProcessHeap(), 0, service->username);
  295. if (service->password) {
  296. SecureZeroMemory(service->password, service->passwordlen);
  297. HeapFree(GetProcessHeap(), 0, service->password);
  298. }
  299. if (service->env) HeapFree(GetProcessHeap(), 0, service->env);
  300. if (service->env_extra) HeapFree(GetProcessHeap(), 0, service->env_extra);
  301. if (service->handle) CloseServiceHandle(service->handle);
  302. if (service->process_handle) CloseHandle(service->process_handle);
  303. if (service->wait_handle) UnregisterWait(service->process_handle);
  304. if (service->throttle_section_initialised) DeleteCriticalSection(&service->throttle_section);
  305. if (service->throttle_timer) CloseHandle(service->throttle_timer);
  306. HeapFree(GetProcessHeap(), 0, service);
  307. }
  308. /* About to install the service */
  309. int pre_install_service(int argc, TCHAR **argv) {
  310. nssm_service_t *service = alloc_nssm_service();
  311. set_nssm_service_defaults(service);
  312. if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);
  313. /* Show the dialogue box if we didn't give the service name and path */
  314. if (argc < 2) return nssm_gui(IDD_INSTALL, service);
  315. if (! service) {
  316. print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("service"), _T("pre_install_service()"));
  317. return 1;
  318. }
  319. _sntprintf_s(service->exe, _countof(service->exe), _TRUNCATE, _T("%s"), argv[1]);
  320. /* Arguments are optional */
  321. size_t flagslen = 0;
  322. size_t s = 0;
  323. int i;
  324. for (i = 2; i < argc; i++) flagslen += _tcslen(argv[i]) + 1;
  325. if (! flagslen) flagslen = 1;
  326. if (flagslen > _countof(service->flags)) {
  327. print_message(stderr, NSSM_MESSAGE_FLAGS_TOO_LONG);
  328. return 2;
  329. }
  330. for (i = 2; i < argc; i++) {
  331. size_t len = _tcslen(argv[i]);
  332. memmove(service->flags + s, argv[i], len * sizeof(TCHAR));
  333. s += len;
  334. if (i < argc - 1) service->flags[s++] = _T(' ');
  335. }
  336. /* Work out directory name */
  337. _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);
  338. strip_basename(service->dir);
  339. int ret = install_service(service);
  340. cleanup_nssm_service(service);
  341. return ret;
  342. }
  343. /* About to edit the service. */
  344. int pre_edit_service(int argc, TCHAR **argv) {
  345. /* Require service name. */
  346. if (argc < 1) return usage(1);
  347. nssm_service_t *service = alloc_nssm_service();
  348. _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);
  349. /* Open service manager */
  350. SC_HANDLE services = open_service_manager();
  351. if (! services) {
  352. print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
  353. return 2;
  354. }
  355. /* Try to open the service */
  356. service->handle = OpenService(services, service->name, SC_MANAGER_ALL_ACCESS);
  357. if (! service->handle) {
  358. CloseServiceHandle(services);
  359. print_message(stderr, NSSM_MESSAGE_OPENSERVICE_FAILED);
  360. return 3;
  361. }
  362. /* Get system details. */
  363. unsigned long bufsize;
  364. QUERY_SERVICE_CONFIG *qsc = query_service_config(service->name, service->handle);
  365. if (! qsc) {
  366. CloseHandle(service->handle);
  367. CloseServiceHandle(services);
  368. return 4;
  369. }
  370. service->type = qsc->dwServiceType;
  371. if (! (service->type & SERVICE_WIN32_OWN_PROCESS)) {
  372. HeapFree(GetProcessHeap(), 0, qsc);
  373. CloseHandle(service->handle);
  374. CloseServiceHandle(services);
  375. print_message(stderr, NSSM_MESSAGE_CANNOT_EDIT, service->name, _T("SERVICE_WIN32_OWN_PROCESS"), 0);
  376. return 3;
  377. }
  378. if (get_service_startup(service->name, service->handle, qsc, &service->startup)) {
  379. HeapFree(GetProcessHeap(), 0, qsc);
  380. CloseHandle(service->handle);
  381. CloseServiceHandle(services);
  382. return 4;
  383. }
  384. if (get_service_username(service->name, qsc, &service->username, &service->usernamelen)) {
  385. HeapFree(GetProcessHeap(), 0, qsc);
  386. CloseHandle(service->handle);
  387. CloseServiceHandle(services);
  388. return 5;
  389. }
  390. _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), qsc->lpDisplayName);
  391. /* Get the canonical service name. We open it case insensitively. */
  392. bufsize = _countof(service->name);
  393. GetServiceKeyName(services, service->displayname, service->name, &bufsize);
  394. /* Remember the executable in case it isn't NSSM. */
  395. _sntprintf_s(service->image, _countof(service->image), _TRUNCATE, _T("%s"), qsc->lpBinaryPathName);
  396. HeapFree(GetProcessHeap(), 0, qsc);
  397. /* Get extended system details. */
  398. if (get_service_description(service->name, service->handle, _countof(service->description), service->description)) {
  399. CloseHandle(service->handle);
  400. CloseServiceHandle(services);
  401. return 6;
  402. }
  403. /* Get NSSM details. */
  404. get_parameters(service, 0);
  405. CloseServiceHandle(services);
  406. if (! service->exe[0]) {
  407. print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE, service->name, NSSM, service->image);
  408. service->native = true;
  409. }
  410. nssm_gui(IDD_EDIT, service);
  411. return 0;
  412. }
  413. /* About to remove the service */
  414. int pre_remove_service(int argc, TCHAR **argv) {
  415. nssm_service_t *service = alloc_nssm_service();
  416. set_nssm_service_defaults(service);
  417. if (argc) _sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]);
  418. /* Show dialogue box if we didn't pass service name and "confirm" */
  419. if (argc < 2) return nssm_gui(IDD_REMOVE, service);
  420. if (str_equiv(argv[1], _T("confirm"))) {
  421. int ret = remove_service(service);
  422. cleanup_nssm_service(service);
  423. return ret;
  424. }
  425. print_message(stderr, NSSM_MESSAGE_PRE_REMOVE_SERVICE);
  426. return 100;
  427. }
  428. /* Install the service */
  429. int install_service(nssm_service_t *service) {
  430. if (! service) return 1;
  431. /* Open service manager */
  432. SC_HANDLE services = open_service_manager();
  433. if (! services) {
  434. print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
  435. cleanup_nssm_service(service);
  436. return 2;
  437. }
  438. /* Get path of this program */
  439. GetModuleFileName(0, service->image, _countof(service->image));
  440. /* Create the service - settings will be changed in edit_service() */
  441. service->handle = CreateService(services, service->name, service->name, SC_MANAGER_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, service->image, 0, 0, 0, 0, 0);
  442. if (! service->handle) {
  443. print_message(stderr, NSSM_MESSAGE_CREATESERVICE_FAILED);
  444. CloseServiceHandle(services);
  445. return 5;
  446. }
  447. if (edit_service(service, false)) {
  448. DeleteService(service->handle);
  449. CloseServiceHandle(services);
  450. return 6;
  451. }
  452. print_message(stdout, NSSM_MESSAGE_SERVICE_INSTALLED, service->name);
  453. /* Cleanup */
  454. CloseServiceHandle(services);
  455. return 0;
  456. }
  457. /* Edit the service. */
  458. int edit_service(nssm_service_t *service, bool editing) {
  459. if (! service) return 1;
  460. /*
  461. The only two valid flags for service type are SERVICE_WIN32_OWN_PROCESS
  462. and SERVICE_INTERACTIVE_PROCESS.
  463. */
  464. service->type &= SERVICE_INTERACTIVE_PROCESS;
  465. service->type |= SERVICE_WIN32_OWN_PROCESS;
  466. /* Startup type. */
  467. unsigned long startup;
  468. switch (service->startup) {
  469. case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break;
  470. case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break;
  471. default: startup = SERVICE_AUTO_START;
  472. }
  473. /* Display name. */
  474. if (! service->displayname[0]) _sntprintf_s(service->displayname, _countof(service->displayname), _TRUNCATE, _T("%s"), service->name);
  475. /*
  476. Username must be NULL if we aren't changing or an account name.
  477. We must explicitly user LOCALSYSTEM to change it when we are editing.
  478. Password must be NULL if we aren't changing, a password or "".
  479. Empty passwords are valid but we won't allow them in the GUI.
  480. */
  481. TCHAR *username = 0;
  482. TCHAR *password = 0;
  483. if (service->usernamelen) {
  484. username = service->username;
  485. if (service->passwordlen) password = service->password;
  486. else password = _T("");
  487. }
  488. else if (editing) username = NSSM_LOCALSYSTEM_ACCOUNT;
  489. if (grant_logon_as_service(username)) {
  490. print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);
  491. return 5;
  492. }
  493. if (! ChangeServiceConfig(service->handle, service->type, startup, SERVICE_NO_CHANGE, 0, 0, 0, 0, username, password, service->displayname)) {
  494. print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
  495. return 5;
  496. }
  497. if (service->description[0] || editing) {
  498. SERVICE_DESCRIPTION description;
  499. ZeroMemory(&description, sizeof(description));
  500. if (service->description[0]) description.lpDescription = service->description;
  501. else description.lpDescription = 0;
  502. if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_DESCRIPTION, &description)) {
  503. log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DESCRIPTION_FAILED, service->name, error_string(GetLastError()), 0);
  504. }
  505. }
  506. SERVICE_DELAYED_AUTO_START_INFO delayed;
  507. ZeroMemory(&delayed, sizeof(delayed));
  508. if (service->startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;
  509. else delayed.fDelayedAutostart = 0;
  510. /* Delayed startup isn't supported until Vista. */
  511. if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {
  512. unsigned long error = GetLastError();
  513. /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
  514. if (error != ERROR_INVALID_LEVEL) {
  515. log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service->name, error_string(error), 0);
  516. }
  517. }
  518. /* Don't mess with parameters which aren't ours. */
  519. if (! service->native) {
  520. /* Now we need to put the parameters into the registry */
  521. if (create_parameters(service, editing)) {
  522. print_message(stderr, NSSM_MESSAGE_CREATE_PARAMETERS_FAILED);
  523. return 6;
  524. }
  525. set_service_recovery(service);
  526. }
  527. return 0;
  528. }
  529. /* Remove the service */
  530. int remove_service(nssm_service_t *service) {
  531. if (! service) return 1;
  532. /* Open service manager */
  533. SC_HANDLE services = open_service_manager();
  534. if (! services) {
  535. print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
  536. return 2;
  537. }
  538. /* Try to open the service */
  539. service->handle = OpenService(services, service->name, SC_MANAGER_ALL_ACCESS);
  540. if (! service->handle) {
  541. print_message(stderr, NSSM_MESSAGE_OPENSERVICE_FAILED);
  542. CloseServiceHandle(services);
  543. return 3;
  544. }
  545. /* Get the canonical service name. We open it case insensitively. */
  546. unsigned long bufsize = _countof(service->displayname);
  547. GetServiceDisplayName(services, service->name, service->displayname, &bufsize);
  548. bufsize = _countof(service->name);
  549. GetServiceKeyName(services, service->displayname, service->name, &bufsize);
  550. /* Try to delete the service */
  551. if (! DeleteService(service->handle)) {
  552. print_message(stderr, NSSM_MESSAGE_DELETESERVICE_FAILED);
  553. CloseServiceHandle(services);
  554. return 4;
  555. }
  556. /* Cleanup */
  557. CloseServiceHandle(services);
  558. print_message(stdout, NSSM_MESSAGE_SERVICE_REMOVED, service->name);
  559. return 0;
  560. }
  561. /* Service initialisation */
  562. void WINAPI service_main(unsigned long argc, TCHAR **argv) {
  563. nssm_service_t *service = alloc_nssm_service();
  564. if (! service) return;
  565. if (_sntprintf_s(service->name, _countof(service->name), _TRUNCATE, _T("%s"), argv[0]) < 0) {
  566. log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("service->name"), _T("service_main()"), 0);
  567. return;
  568. }
  569. /* We can use a condition variable in a critical section on Vista or later. */
  570. if (imports.SleepConditionVariableCS && imports.WakeConditionVariable) use_critical_section = true;
  571. else use_critical_section = false;
  572. /* Initialise status */
  573. ZeroMemory(&service->status, sizeof(service->status));
  574. service->status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
  575. service->status.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;
  576. service->status.dwWin32ExitCode = NO_ERROR;
  577. service->status.dwServiceSpecificExitCode = 0;
  578. service->status.dwCheckPoint = 0;
  579. service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
  580. /* Signal we AREN'T running the server */
  581. service->process_handle = 0;
  582. service->pid = 0;
  583. /* Register control handler */
  584. service->status_handle = RegisterServiceCtrlHandlerEx(NSSM, service_control_handler, (void *) service);
  585. if (! service->status_handle) {
  586. log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_REGISTERSERVICECTRLHANDER_FAILED, error_string(GetLastError()), 0);
  587. return;
  588. }
  589. log_service_control(service->name, 0, true);
  590. service->status.dwCurrentState = SERVICE_START_PENDING;
  591. service->status.dwWaitHint = service->throttle_delay + NSSM_WAITHINT_MARGIN;
  592. SetServiceStatus(service->status_handle, &service->status);
  593. if (is_admin) {
  594. /* Try to create the exit action parameters; we don't care if it fails */
  595. create_exit_action(service->name, exit_action_strings[0], false);
  596. SC_HANDLE services = open_service_manager();
  597. if (services) {
  598. service->handle = OpenService(services, service->name, SC_MANAGER_ALL_ACCESS);
  599. set_service_recovery(service);
  600. CloseServiceHandle(services);
  601. }
  602. }
  603. /* Used for signalling a resume if the service pauses when throttled. */
  604. if (use_critical_section) {
  605. InitializeCriticalSection(&service->throttle_section);
  606. service->throttle_section_initialised = true;
  607. }
  608. else {
  609. service->throttle_timer = CreateWaitableTimer(0, 1, 0);
  610. if (! service->throttle_timer) {
  611. log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_CREATEWAITABLETIMER_FAILED, service->name, error_string(GetLastError()), 0);
  612. }
  613. }
  614. monitor_service(service);
  615. }
  616. /* Make sure service recovery actions are taken where necessary */
  617. void set_service_recovery(nssm_service_t *service) {
  618. SERVICE_FAILURE_ACTIONS_FLAG flag;
  619. ZeroMemory(&flag, sizeof(flag));
  620. flag.fFailureActionsOnNonCrashFailures = true;
  621. /* This functionality was added in Vista so the call may fail */
  622. if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &flag)) {
  623. unsigned long error = GetLastError();
  624. /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
  625. if (error != ERROR_INVALID_LEVEL) {
  626. log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SERVICE_CONFIG_FAILURE_ACTIONS_FAILED, service->name, error_string(error), 0);
  627. }
  628. }
  629. }
  630. int monitor_service(nssm_service_t *service) {
  631. /* Set service status to started */
  632. int ret = start_service(service);
  633. if (ret) {
  634. TCHAR code[16];
  635. _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%d"), ret);
  636. log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, service->exe, service->name, ret, 0);
  637. return ret;
  638. }
  639. log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, service->exe, service->flags, service->name, service->dir, 0);
  640. /* Monitor service */
  641. if (! RegisterWaitForSingleObject(&service->wait_handle, service->process_handle, end_service, (void *) service, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {
  642. log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service->name, service->exe, error_string(GetLastError()), 0);
  643. }
  644. return 0;
  645. }
  646. TCHAR *service_control_text(unsigned long control) {
  647. switch (control) {
  648. /* HACK: there is no SERVICE_CONTROL_START constant */
  649. case 0: return _T("START");
  650. case SERVICE_CONTROL_STOP: return _T("STOP");
  651. case SERVICE_CONTROL_SHUTDOWN: return _T("SHUTDOWN");
  652. case SERVICE_CONTROL_PAUSE: return _T("PAUSE");
  653. case SERVICE_CONTROL_CONTINUE: return _T("CONTINUE");
  654. case SERVICE_CONTROL_INTERROGATE: return _T("INTERROGATE");
  655. default: return 0;
  656. }
  657. }
  658. void log_service_control(TCHAR *service_name, unsigned long control, bool handled) {
  659. TCHAR *text = service_control_text(control);
  660. unsigned long event;
  661. if (! text) {
  662. /* "0x" + 8 x hex + NULL */
  663. text = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, 11 * sizeof(TCHAR));
  664. if (! text) {
  665. log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
  666. return;
  667. }
  668. if (_sntprintf_s(text, 11, _TRUNCATE, _T("0x%08x"), control) < 0) {
  669. log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("control code"), _T("log_service_control()"), 0);
  670. HeapFree(GetProcessHeap(), 0, text);
  671. return;
  672. }
  673. event = NSSM_EVENT_SERVICE_CONTROL_UNKNOWN;
  674. }
  675. else if (handled) event = NSSM_EVENT_SERVICE_CONTROL_HANDLED;
  676. else event = NSSM_EVENT_SERVICE_CONTROL_NOT_HANDLED;
  677. log_event(EVENTLOG_INFORMATION_TYPE, event, service_name, text, 0);
  678. if (event == NSSM_EVENT_SERVICE_CONTROL_UNKNOWN) {
  679. HeapFree(GetProcessHeap(), 0, text);
  680. }
  681. }
  682. /* Service control handler */
  683. unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {
  684. nssm_service_t *service = (nssm_service_t *) context;
  685. switch (control) {
  686. case SERVICE_CONTROL_INTERROGATE:
  687. /* We always keep the service status up-to-date so this is a no-op. */
  688. return NO_ERROR;
  689. case SERVICE_CONTROL_SHUTDOWN:
  690. case SERVICE_CONTROL_STOP:
  691. log_service_control(service->name, control, true);
  692. /*
  693. We MUST acknowledge the stop request promptly but we're committed to
  694. waiting for the application to exit. Spawn a new thread to wait
  695. while we acknowledge the request.
  696. */
  697. if (! CreateThread(NULL, 0, shutdown_service, context, 0, NULL)) {
  698. log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);
  699. /*
  700. We couldn't create a thread to tidy up so we'll have to force the tidyup
  701. to complete in time in this thread.
  702. */
  703. service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
  704. service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
  705. service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
  706. stop_service(service, 0, true, true);
  707. }
  708. return NO_ERROR;
  709. case SERVICE_CONTROL_CONTINUE:
  710. log_service_control(service->name, control, true);
  711. service->throttle = 0;
  712. if (use_critical_section) imports.WakeConditionVariable(&service->throttle_condition);
  713. else {
  714. if (! service->throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED;
  715. ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
  716. SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
  717. }
  718. service->status.dwCurrentState = SERVICE_CONTINUE_PENDING;
  719. service->status.dwWaitHint = throttle_milliseconds(service->throttle) + NSSM_WAITHINT_MARGIN;
  720. log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service->name, 0);
  721. SetServiceStatus(service->status_handle, &service->status);
  722. return NO_ERROR;
  723. case SERVICE_CONTROL_PAUSE:
  724. /*
  725. We don't accept pause messages but it isn't possible to register
  726. only for continue messages so we have to handle this case.
  727. */
  728. log_service_control(service->name, control, false);
  729. return ERROR_CALL_NOT_IMPLEMENTED;
  730. }
  731. /* Unknown control */
  732. log_service_control(service->name, control, false);
  733. return ERROR_CALL_NOT_IMPLEMENTED;
  734. }
  735. /* Start the service */
  736. int start_service(nssm_service_t *service) {
  737. service->stopping = false;
  738. service->allow_restart = true;
  739. if (service->process_handle) return 0;
  740. /* Allocate a STARTUPINFO structure for a new process */
  741. STARTUPINFO si;
  742. ZeroMemory(&si, sizeof(si));
  743. si.cb = sizeof(si);
  744. /* Allocate a PROCESSINFO structure for the process */
  745. PROCESS_INFORMATION pi;
  746. ZeroMemory(&pi, sizeof(pi));
  747. /* Get startup parameters */
  748. int ret = get_parameters(service, &si);
  749. if (ret) {
  750. log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service->name, 0);
  751. return stop_service(service, 2, true, true);
  752. }
  753. /* Launch executable with arguments */
  754. TCHAR cmd[CMD_LENGTH];
  755. if (_sntprintf_s(cmd, _countof(cmd), _TRUNCATE, _T("\"%s\" %s"), service->exe, service->flags) < 0) {
  756. log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, _T("command line"), _T("start_service"), 0);
  757. close_output_handles(&si);
  758. return stop_service(service, 2, true, true);
  759. }
  760. throttle_restart(service);
  761. bool inherit_handles = false;
  762. if (si.dwFlags & STARTF_USESTDHANDLES) inherit_handles = true;
  763. unsigned long flags = 0;
  764. #ifdef UNICODE
  765. flags |= CREATE_UNICODE_ENVIRONMENT;
  766. #endif
  767. if (! CreateProcess(0, cmd, 0, 0, inherit_handles, flags, service->env, service->dir, &si, &pi)) {
  768. unsigned long exitcode = 3;
  769. unsigned long error = GetLastError();
  770. if (error == ERROR_INVALID_PARAMETER && service->env) {
  771. log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED_INVALID_ENVIRONMENT, service->name, service->exe, NSSM_REG_ENV, 0);
  772. if (test_environment(service->env)) exitcode = 4;
  773. }
  774. else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service->name, service->exe, error_string(error), 0);
  775. close_output_handles(&si);
  776. return stop_service(service, exitcode, true, true);
  777. }
  778. service->process_handle = pi.hProcess;
  779. service->pid = pi.dwProcessId;
  780. if (get_process_creation_time(service->process_handle, &service->creation_time)) ZeroMemory(&service->creation_time, sizeof(service->creation_time));
  781. close_output_handles(&si);
  782. /*
  783. Wait for a clean startup before changing the service status to RUNNING
  784. but be mindful of the fact that we are blocking the service control manager
  785. so abandon the wait before too much time has elapsed.
  786. */
  787. unsigned long delay = service->throttle_delay;
  788. if (delay > NSSM_SERVICE_STATUS_DEADLINE) {
  789. TCHAR delay_milliseconds[16];
  790. _sntprintf_s(delay_milliseconds, _countof(delay_milliseconds), _TRUNCATE, _T("%lu"), delay);
  791. TCHAR deadline_milliseconds[16];
  792. _sntprintf_s(deadline_milliseconds, _countof(deadline_milliseconds), _TRUNCATE, _T("%lu"), NSSM_SERVICE_STATUS_DEADLINE);
  793. log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_STARTUP_DELAY_TOO_LONG, service->name, delay_milliseconds, NSSM, deadline_milliseconds, 0);
  794. delay = NSSM_SERVICE_STATUS_DEADLINE;
  795. }
  796. unsigned long deadline = WaitForSingleObject(service->process_handle, delay);
  797. /* Signal successful start */
  798. service->status.dwCurrentState = SERVICE_RUNNING;
  799. SetServiceStatus(service->status_handle, &service->status);
  800. /* Continue waiting for a clean startup. */
  801. if (deadline == WAIT_TIMEOUT) {
  802. if (service->throttle_delay > delay) {
  803. if (WaitForSingleObject(service->process_handle, service->throttle_delay - delay) == WAIT_TIMEOUT) service->throttle = 0;
  804. }
  805. else service->throttle = 0;
  806. }
  807. return 0;
  808. }
  809. /* Stop the service */
  810. int stop_service(nssm_service_t *service, unsigned long exitcode, bool graceful, bool default_action) {
  811. service->allow_restart = false;
  812. if (service->wait_handle) {
  813. UnregisterWait(service->wait_handle);
  814. service->wait_handle = 0;
  815. }
  816. if (default_action && ! exitcode && ! graceful) {
  817. log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_GRACEFUL_SUICIDE, service->name, service->exe, exit_action_strings[NSSM_EXIT_UNCLEAN], exit_action_strings[NSSM_EXIT_UNCLEAN], exit_action_strings[NSSM_EXIT_UNCLEAN], exit_action_strings[NSSM_EXIT_REALLY], 0);
  818. graceful = true;
  819. }
  820. /* Signal we are stopping */
  821. if (graceful) {
  822. service->status.dwCurrentState = SERVICE_STOP_PENDING;
  823. service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
  824. SetServiceStatus(service->status_handle, &service->status);
  825. }
  826. /* Nothing to do if service isn't running */
  827. if (service->pid) {
  828. /* Shut down service */
  829. log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service->name, service->exe, 0);
  830. kill_process(service, service->process_handle, service->pid, 0);
  831. }
  832. else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service->name, service->exe, 0);
  833. end_service((void *) service, true);
  834. /* Signal we stopped */
  835. if (graceful) {
  836. service->status.dwCurrentState = SERVICE_STOPPED;
  837. if (exitcode) {
  838. service->status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
  839. service->status.dwServiceSpecificExitCode = exitcode;
  840. }
  841. else {
  842. service->status.dwWin32ExitCode = NO_ERROR;
  843. service->status.dwServiceSpecificExitCode = 0;
  844. }
  845. SetServiceStatus(service->status_handle, &service->status);
  846. }
  847. return exitcode;
  848. }
  849. /* Callback function triggered when the server exits */
  850. void CALLBACK end_service(void *arg, unsigned char why) {
  851. nssm_service_t *service = (nssm_service_t *) arg;
  852. if (service->stopping) return;
  853. service->stopping = true;
  854. /* Check exit code */
  855. unsigned long exitcode = 0;
  856. TCHAR code[16];
  857. if (service->process_handle) {
  858. GetExitCodeProcess(service->process_handle, &exitcode);
  859. if (exitcode == STILL_ACTIVE || get_process_exit_time(service->process_handle, &service->exit_time)) GetSystemTimeAsFileTime(&service->exit_time);
  860. CloseHandle(service->process_handle);
  861. }
  862. else GetSystemTimeAsFileTime(&service->exit_time);
  863. service->process_handle = 0;
  864. /*
  865. Log that the service ended BEFORE logging about killing the process
  866. tree. See below for the possible values of the why argument.
  867. */
  868. if (! why) {
  869. _sntprintf_s(code, _countof(code), _TRUNCATE, _T("%lu"), exitcode);
  870. log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, service->exe, service->name, code, 0);
  871. }
  872. /* Clean up. */
  873. if (exitcode == STILL_ACTIVE) exitcode = 0;
  874. if (service->pid) kill_process_tree(service, service->pid, exitcode, service->pid);
  875. service->pid = 0;
  876. /*
  877. The why argument is true if our wait timed out or false otherwise.
  878. Our wait is infinite so why will never be true when called by the system.
  879. If it is indeed true, assume we were called from stop_service() because
  880. this is a controlled shutdown, and don't take any restart action.
  881. */
  882. if (why) return;
  883. if (! service->allow_restart) return;
  884. /* What action should we take? */
  885. int action = NSSM_EXIT_RESTART;
  886. TCHAR action_string[ACTION_LEN];
  887. bool default_action;
  888. if (! get_exit_action(service->name, &exitcode, action_string, &default_action)) {
  889. for (int i = 0; exit_action_strings[i]; i++) {
  890. if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
  891. action = i;
  892. break;
  893. }
  894. }
  895. }
  896. switch (action) {
  897. /* Try to restart the service or return failure code to service manager */
  898. case NSSM_EXIT_RESTART:
  899. log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service->name, code, exit_action_strings[action], service->exe, 0);
  900. while (monitor_service(service)) {
  901. log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, service->exe, service->name, 0);
  902. Sleep(30000);
  903. }
  904. break;
  905. /* Do nothing, just like srvany would */
  906. case NSSM_EXIT_IGNORE:
  907. log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service->name, code, exit_action_strings[action], service->exe, 0);
  908. Sleep(INFINITE);
  909. break;
  910. /* Tell the service manager we are finished */
  911. case NSSM_EXIT_REALLY:
  912. log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service->name, code, exit_action_strings[action], 0);
  913. stop_service(service, exitcode, true, default_action);
  914. break;
  915. /* Fake a crash so pre-Vista service managers will run recovery actions. */
  916. case NSSM_EXIT_UNCLEAN:
  917. log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service->name, code, exit_action_strings[action], 0);
  918. stop_service(service, exitcode, false, default_action);
  919. free_imports();
  920. exit(exitcode);
  921. break;
  922. }
  923. }
  924. void throttle_restart(nssm_service_t *service) {
  925. /* This can't be a restart if the service is already running. */
  926. if (! service->throttle++) return;
  927. int ms = throttle_milliseconds(service->throttle);
  928. if (service->throttle > 7) service->throttle = 8;
  929. TCHAR threshold[8], milliseconds[8];
  930. _sntprintf_s(threshold, _countof(threshold), _TRUNCATE, _T("%lu"), service->throttle_delay);
  931. _sntprintf_s(milliseconds, _countof(milliseconds), _TRUNCATE, _T("%lu"), ms);
  932. log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service->name, threshold, milliseconds, 0);
  933. if (use_critical_section) EnterCriticalSection(&service->throttle_section);
  934. else if (service->throttle_timer) {
  935. ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
  936. service->throttle_duetime.QuadPart = 0 - (ms * 10000LL);
  937. SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
  938. }
  939. service->status.dwCurrentState = SERVICE_PAUSED;
  940. SetServiceStatus(service->status_handle, &service->status);
  941. if (use_critical_section) {
  942. imports.SleepConditionVariableCS(&service->throttle_condition, &service->throttle_section, ms);
  943. LeaveCriticalSection(&service->throttle_section);
  944. }
  945. else {
  946. if (service->throttle_timer) WaitForSingleObject(service->throttle_timer, INFINITE);
  947. else Sleep(ms);
  948. }
  949. }
  950. /*
  951. When responding to a stop (or any other) request we need to set dwWaitHint to
  952. the number of milliseconds we expect the operation to take, and optionally
  953. increase dwCheckPoint. If dwWaitHint milliseconds elapses without the
  954. operation completing or dwCheckPoint increasing, the system will consider the
  955. service to be hung.
  956. However the system will consider the service to be hung after 30000
  957. milliseconds regardless of the value of dwWaitHint if dwCheckPoint has not
  958. changed. Therefore if we want to wait longer than that we must periodically
  959. increase dwCheckPoint.
  960. Furthermore, it will consider the service to be hung after 60000 milliseconds
  961. regardless of the value of dwCheckPoint unless dwWaitHint is increased every
  962. time dwCheckPoint is also increased.
  963. Our strategy then is to retrieve the initial dwWaitHint and wait for
  964. NSSM_SERVICE_STATUS_DEADLINE milliseconds. If the process is still running
  965. and we haven't finished waiting we increment dwCheckPoint and add whichever is
  966. smaller of NSSM_SERVICE_STATUS_DEADLINE or the remaining timeout to
  967. dwWaitHint.
  968. Only doing both these things will prevent the system from killing the service.
  969. Returns: 1 if the wait timed out.
  970. 0 if the wait completed.
  971. -1 on error.
  972. */
  973. int await_shutdown(nssm_service_t *service, TCHAR *function_name, unsigned long timeout) {
  974. unsigned long interval;
  975. unsigned long waithint;
  976. unsigned long ret;
  977. unsigned long waited;
  978. TCHAR interval_milliseconds[16];
  979. TCHAR timeout_milliseconds[16];
  980. TCHAR waited_milliseconds[16];
  981. TCHAR *function = function_name;
  982. /* Add brackets to function name. */
  983. size_t funclen = _tcslen(function_name) + 3;
  984. TCHAR *func = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, funclen * sizeof(TCHAR));
  985. if (func) {
  986. if (_sntprintf_s(func, funclen, _TRUNCATE, _T("%s()"), function_name) > -1) function = func;
  987. }
  988. _sntprintf_s(timeout_milliseconds, _countof(timeout_milliseconds), _TRUNCATE, _T("%lu"), timeout);
  989. waithint = service->status.dwWaitHint;
  990. waited = 0;
  991. while (waited < timeout) {
  992. interval = timeout - waited;
  993. if (interval > NSSM_SERVICE_STATUS_DEADLINE) interval = NSSM_SERVICE_STATUS_DEADLINE;
  994. service->status.dwCurrentState = SERVICE_STOP_PENDING;
  995. service->status.dwWaitHint += interval;
  996. service->status.dwCheckPoint++;
  997. SetServiceStatus(service->status_handle, &service->status);
  998. if (waited) {
  999. _sntprintf_s(waited_milliseconds, _countof(waited_milliseconds), _TRUNCATE, _T("%lu"), waited);
  1000. _sntprintf_s(interval_milliseconds, _countof(interval_milliseconds), _TRUNCATE, _T("%lu"), interval);
  1001. log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_AWAITING_SHUTDOWN, function, service->name, waited_milliseconds, interval_milliseconds, timeout_milliseconds, 0);
  1002. }
  1003. switch (WaitForSingleObject(service->process_handle, interval)) {
  1004. case WAIT_OBJECT_0:
  1005. ret = 0;
  1006. goto awaited;
  1007. case WAIT_TIMEOUT:
  1008. ret = 1;
  1009. break;
  1010. default:
  1011. ret = -1;
  1012. goto awaited;
  1013. }
  1014. waited += interval;
  1015. }
  1016. awaited:
  1017. if (func) HeapFree(GetProcessHeap(), 0, func);
  1018. return ret;
  1019. }