service.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687
  1. #include "nssm.h"
  2. bool is_admin;
  3. SERVICE_STATUS service_status;
  4. SERVICE_STATUS_HANDLE service_handle;
  5. HANDLE process_handle;
  6. HANDLE wait_handle;
  7. unsigned long pid;
  8. static char service_name[SERVICE_NAME_LENGTH];
  9. char exe[EXE_LENGTH];
  10. char flags[CMD_LENGTH];
  11. char dir[MAX_PATH];
  12. bool stopping;
  13. bool allow_restart;
  14. unsigned long throttle_delay;
  15. unsigned long stop_method;
  16. unsigned long kill_console_delay;
  17. unsigned long kill_window_delay;
  18. unsigned long kill_threads_delay;
  19. CRITICAL_SECTION throttle_section;
  20. CONDITION_VARIABLE throttle_condition;
  21. HANDLE throttle_timer;
  22. LARGE_INTEGER throttle_duetime;
  23. bool use_critical_section;
  24. FILETIME creation_time;
  25. extern imports_t imports;
  26. static enum { NSSM_EXIT_RESTART, NSSM_EXIT_IGNORE, NSSM_EXIT_REALLY, NSSM_EXIT_UNCLEAN } exit_actions;
  27. static const char *exit_action_strings[] = { "Restart", "Ignore", "Exit", "Suicide", 0 };
  28. static unsigned long throttle;
  29. static inline int throttle_milliseconds() {
  30. /* pow() operates on doubles. */
  31. int ret = 1; for (unsigned long i = 1; i < throttle; i++) ret *= 2;
  32. return ret * 1000;
  33. }
  34. /* Connect to the service manager */
  35. SC_HANDLE open_service_manager() {
  36. SC_HANDLE ret = OpenSCManager(0, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS);
  37. if (! ret) {
  38. if (is_admin) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENSCMANAGER_FAILED, 0);
  39. return 0;
  40. }
  41. return ret;
  42. }
  43. /* About to install the service */
  44. int pre_install_service(int argc, char **argv) {
  45. /* Show the dialogue box if we didn't give the service name and path */
  46. if (argc < 2) return nssm_gui(IDD_INSTALL, argv[0]);
  47. /* Arguments are optional */
  48. char *flags;
  49. size_t flagslen = 0;
  50. size_t s = 0;
  51. int i;
  52. for (i = 2; i < argc; i++) flagslen += strlen(argv[i]) + 1;
  53. if (! flagslen) flagslen = 1;
  54. flags = (char *) HeapAlloc(GetProcessHeap(), 0, flagslen);
  55. if (! flags) {
  56. log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "flags", "pre_install_service()", 0);
  57. return 2;
  58. }
  59. ZeroMemory(flags, flagslen);
  60. /*
  61. This probably isn't UTF8-safe and should use std::string or something
  62. but it's been broken for the best part of a decade and due for a rewrite
  63. anyway so it'll do as a quick-'n'-dirty fix. Note that we don't free
  64. the flags buffer but as the program exits that isn't a big problem.
  65. */
  66. for (i = 2; i < argc; i++) {
  67. size_t len = strlen(argv[i]);
  68. memmove(flags + s, argv[i], len);
  69. s += len;
  70. if (i < argc - 1) flags[s++] = ' ';
  71. }
  72. return install_service(argv[0], argv[1], flags);
  73. }
  74. /* About to remove the service */
  75. int pre_remove_service(int argc, char **argv) {
  76. /* Show dialogue box if we didn't pass service name and "confirm" */
  77. if (argc < 2) return nssm_gui(IDD_REMOVE, argv[0]);
  78. if (str_equiv(argv[1], "confirm")) return remove_service(argv[0]);
  79. print_message(stderr, NSSM_MESSAGE_PRE_REMOVE_SERVICE);
  80. return 100;
  81. }
  82. /* Install the service */
  83. int install_service(char *name, char *exe, char *flags) {
  84. /* Open service manager */
  85. SC_HANDLE services = open_service_manager();
  86. if (! services) {
  87. print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
  88. return 2;
  89. }
  90. /* Get path of this program */
  91. char path[MAX_PATH];
  92. GetModuleFileName(0, path, MAX_PATH);
  93. /* Construct command */
  94. char command[CMD_LENGTH];
  95. size_t pathlen = strlen(path);
  96. if (pathlen + 1 >= VALUE_LENGTH) {
  97. print_message(stderr, NSSM_MESSAGE_PATH_TOO_LONG, NSSM);
  98. return 3;
  99. }
  100. if (_snprintf_s(command, sizeof(command), _TRUNCATE, "\"%s\"", path) < 0) {
  101. print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY_FOR_IMAGEPATH);
  102. return 4;
  103. }
  104. /* Work out directory name */
  105. size_t len = strlen(exe);
  106. size_t i;
  107. for (i = len; i && exe[i] != '\\' && exe[i] != '/'; i--);
  108. char dir[MAX_PATH];
  109. memmove(dir, exe, i);
  110. dir[i] = '\0';
  111. /* Create the service */
  112. SC_HANDLE service = CreateService(services, name, name, SC_MANAGER_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, command, 0, 0, 0, 0, 0);
  113. if (! service) {
  114. print_message(stderr, NSSM_MESSAGE_CREATESERVICE_FAILED);
  115. CloseServiceHandle(services);
  116. return 5;
  117. }
  118. /* Now we need to put the parameters into the registry */
  119. if (create_parameters(name, exe, flags, dir)) {
  120. print_message(stderr, NSSM_MESSAGE_CREATE_PARAMETERS_FAILED);
  121. DeleteService(service);
  122. CloseServiceHandle(services);
  123. return 6;
  124. }
  125. set_service_recovery(service, name);
  126. /* Cleanup */
  127. CloseServiceHandle(service);
  128. CloseServiceHandle(services);
  129. print_message(stdout, NSSM_MESSAGE_SERVICE_INSTALLED, name);
  130. return 0;
  131. }
  132. /* Remove the service */
  133. int remove_service(char *name) {
  134. /* Open service manager */
  135. SC_HANDLE services = open_service_manager();
  136. if (! services) {
  137. print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
  138. return 2;
  139. }
  140. /* Try to open the service */
  141. SC_HANDLE service = OpenService(services, name, SC_MANAGER_ALL_ACCESS);
  142. if (! service) {
  143. print_message(stderr, NSSM_MESSAGE_OPENSERVICE_FAILED);
  144. CloseServiceHandle(services);
  145. return 3;
  146. }
  147. /* Try to delete the service */
  148. if (! DeleteService(service)) {
  149. print_message(stderr, NSSM_MESSAGE_DELETESERVICE_FAILED);
  150. CloseServiceHandle(service);
  151. CloseServiceHandle(services);
  152. return 4;
  153. }
  154. /* Cleanup */
  155. CloseServiceHandle(service);
  156. CloseServiceHandle(services);
  157. print_message(stdout, NSSM_MESSAGE_SERVICE_REMOVED, name);
  158. return 0;
  159. }
  160. /* Service initialisation */
  161. void WINAPI service_main(unsigned long argc, char **argv) {
  162. if (_snprintf_s(service_name, sizeof(service_name), _TRUNCATE, "%s", argv[0]) < 0) {
  163. log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "service_name", "service_main()", 0);
  164. return;
  165. }
  166. /* We can use a condition variable in a critical section on Vista or later. */
  167. if (imports.SleepConditionVariableCS && imports.WakeConditionVariable) use_critical_section = true;
  168. else use_critical_section = false;
  169. /* Initialise status */
  170. ZeroMemory(&service_status, sizeof(service_status));
  171. service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
  172. service_status.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;
  173. service_status.dwWin32ExitCode = NO_ERROR;
  174. service_status.dwServiceSpecificExitCode = 0;
  175. service_status.dwCheckPoint = 0;
  176. service_status.dwWaitHint = NSSM_WAITHINT_MARGIN;
  177. /* Signal we AREN'T running the server */
  178. process_handle = 0;
  179. pid = 0;
  180. /* Register control handler */
  181. service_handle = RegisterServiceCtrlHandlerEx(NSSM, service_control_handler, 0);
  182. if (! service_handle) {
  183. log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_REGISTERSERVICECTRLHANDER_FAILED, error_string(GetLastError()), 0);
  184. return;
  185. }
  186. log_service_control(service_name, 0, true);
  187. service_status.dwCurrentState = SERVICE_START_PENDING;
  188. service_status.dwWaitHint = throttle_delay + NSSM_WAITHINT_MARGIN;
  189. SetServiceStatus(service_handle, &service_status);
  190. if (is_admin) {
  191. /* Try to create the exit action parameters; we don't care if it fails */
  192. create_exit_action(argv[0], exit_action_strings[0]);
  193. set_service_recovery(0, service_name);
  194. }
  195. /* Used for signalling a resume if the service pauses when throttled. */
  196. if (use_critical_section) InitializeCriticalSection(&throttle_section);
  197. else {
  198. throttle_timer = CreateWaitableTimer(0, 1, 0);
  199. if (! throttle_timer) {
  200. log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_CREATEWAITABLETIMER_FAILED, service_name, error_string(GetLastError()), 0);
  201. }
  202. }
  203. monitor_service();
  204. }
  205. /* Make sure service recovery actions are taken where necessary */
  206. void set_service_recovery(SC_HANDLE service, char *service_name) {
  207. SC_HANDLE services = 0;
  208. if (! service) {
  209. services = open_service_manager();
  210. if (! services) return;
  211. service = OpenService(services, service_name, SC_MANAGER_ALL_ACCESS);
  212. if (! service) return;
  213. }
  214. SERVICE_FAILURE_ACTIONS_FLAG flag;
  215. ZeroMemory(&flag, sizeof(flag));
  216. flag.fFailureActionsOnNonCrashFailures = true;
  217. /* This functionality was added in Vista so the call may fail */
  218. if (! ChangeServiceConfig2(service, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &flag)) {
  219. unsigned long error = GetLastError();
  220. /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
  221. if (error != ERROR_INVALID_LEVEL) {
  222. log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CHANGESERVICECONFIG2_FAILED, service_name, error_string(error), 0);
  223. }
  224. }
  225. if (services) {
  226. CloseServiceHandle(service);
  227. CloseServiceHandle(services);
  228. }
  229. }
  230. int monitor_service() {
  231. /* Set service status to started */
  232. int ret = start_service();
  233. if (ret) {
  234. char code[16];
  235. _snprintf_s(code, sizeof(code), _TRUNCATE, "%d", ret);
  236. log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, exe, service_name, ret, 0);
  237. return ret;
  238. }
  239. log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, exe, flags, service_name, dir, 0);
  240. /* Monitor service */
  241. if (! RegisterWaitForSingleObject(&wait_handle, process_handle, end_service, (void *) pid, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {
  242. log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service_name, exe, error_string(GetLastError()), 0);
  243. }
  244. return 0;
  245. }
  246. char *service_control_text(unsigned long control) {
  247. switch (control) {
  248. /* HACK: there is no SERVICE_CONTROL_START constant */
  249. case 0: return "START";
  250. case SERVICE_CONTROL_STOP: return "STOP";
  251. case SERVICE_CONTROL_SHUTDOWN: return "SHUTDOWN";
  252. case SERVICE_CONTROL_PAUSE: return "PAUSE";
  253. case SERVICE_CONTROL_CONTINUE: return "CONTINUE";
  254. case SERVICE_CONTROL_INTERROGATE: return "INTERROGATE";
  255. default: return 0;
  256. }
  257. }
  258. void log_service_control(char *service_name, unsigned long control, bool handled) {
  259. char *text = service_control_text(control);
  260. unsigned long event;
  261. if (! text) {
  262. /* "0x" + 8 x hex + NULL */
  263. text = (char *) HeapAlloc(GetProcessHeap(), 0, 11);
  264. if (! text) {
  265. log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "control code", "log_service_control()", 0);
  266. return;
  267. }
  268. if (_snprintf_s(text, 11, _TRUNCATE, "0x%08x", control) < 0) {
  269. log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "control code", "log_service_control()", 0);
  270. HeapFree(GetProcessHeap(), 0, text);
  271. return;
  272. }
  273. event = NSSM_EVENT_SERVICE_CONTROL_UNKNOWN;
  274. }
  275. else if (handled) event = NSSM_EVENT_SERVICE_CONTROL_HANDLED;
  276. else event = NSSM_EVENT_SERVICE_CONTROL_NOT_HANDLED;
  277. log_event(EVENTLOG_INFORMATION_TYPE, event, service_name, text, 0);
  278. if (event == NSSM_EVENT_SERVICE_CONTROL_UNKNOWN) {
  279. HeapFree(GetProcessHeap(), 0, text);
  280. }
  281. }
  282. /* Service control handler */
  283. unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {
  284. switch (control) {
  285. case SERVICE_CONTROL_INTERROGATE:
  286. /* We always keep the service status up-to-date so this is a no-op. */
  287. return NO_ERROR;
  288. case SERVICE_CONTROL_SHUTDOWN:
  289. case SERVICE_CONTROL_STOP:
  290. log_service_control(service_name, control, true);
  291. stop_service(0, true, true);
  292. return NO_ERROR;
  293. case SERVICE_CONTROL_CONTINUE:
  294. log_service_control(service_name, control, true);
  295. throttle = 0;
  296. if (use_critical_section) imports.WakeConditionVariable(&throttle_condition);
  297. else {
  298. if (! throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED;
  299. ZeroMemory(&throttle_duetime, sizeof(throttle_duetime));
  300. SetWaitableTimer(throttle_timer, &throttle_duetime, 0, 0, 0, 0);
  301. }
  302. service_status.dwCurrentState = SERVICE_CONTINUE_PENDING;
  303. service_status.dwWaitHint = throttle_milliseconds() + NSSM_WAITHINT_MARGIN;
  304. log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service_name, 0);
  305. SetServiceStatus(service_handle, &service_status);
  306. return NO_ERROR;
  307. case SERVICE_CONTROL_PAUSE:
  308. /*
  309. We don't accept pause messages but it isn't possible to register
  310. only for continue messages so we have to handle this case.
  311. */
  312. log_service_control(service_name, control, false);
  313. return ERROR_CALL_NOT_IMPLEMENTED;
  314. }
  315. /* Unknown control */
  316. log_service_control(service_name, control, false);
  317. return ERROR_CALL_NOT_IMPLEMENTED;
  318. }
  319. /* Start the service */
  320. int start_service() {
  321. stopping = false;
  322. allow_restart = true;
  323. if (process_handle) return 0;
  324. /* Allocate a STARTUPINFO structure for a new process */
  325. STARTUPINFO si;
  326. ZeroMemory(&si, sizeof(si));
  327. si.cb = sizeof(si);
  328. /* Allocate a PROCESSINFO structure for the process */
  329. PROCESS_INFORMATION pi;
  330. ZeroMemory(&pi, sizeof(pi));
  331. /* Get startup parameters */
  332. char *env = 0;
  333. int ret = get_parameters(service_name, exe, sizeof(exe), flags, sizeof(flags), dir, sizeof(dir), &env, &throttle_delay, &stop_method, &kill_console_delay, &kill_window_delay, &kill_threads_delay, &si);
  334. if (ret) {
  335. log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service_name, 0);
  336. return stop_service(2, true, true);
  337. }
  338. /* Launch executable with arguments */
  339. char cmd[CMD_LENGTH];
  340. if (_snprintf_s(cmd, sizeof(cmd), _TRUNCATE, "\"%s\" %s", exe, flags) < 0) {
  341. log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "command line", "start_service", 0);
  342. close_output_handles(&si);
  343. return stop_service(2, true, true);
  344. }
  345. throttle_restart();
  346. bool inherit_handles = false;
  347. if (si.dwFlags & STARTF_USESTDHANDLES) inherit_handles = true;
  348. if (! CreateProcess(0, cmd, 0, 0, inherit_handles, 0, env, dir, &si, &pi)) {
  349. unsigned long error = GetLastError();
  350. if (error == ERROR_INVALID_PARAMETER && env) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED_INVALID_ENVIRONMENT, service_name, exe, NSSM_REG_ENV, 0);
  351. else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service_name, exe, error_string(error), 0);
  352. close_output_handles(&si);
  353. return stop_service(3, true, true);
  354. }
  355. process_handle = pi.hProcess;
  356. pid = pi.dwProcessId;
  357. if (get_process_creation_time(process_handle, &creation_time)) ZeroMemory(&creation_time, sizeof(creation_time));
  358. close_output_handles(&si);
  359. /* Wait for a clean startup. */
  360. if (WaitForSingleObject(process_handle, throttle_delay) == WAIT_TIMEOUT) throttle = 0;
  361. /* Signal successful start */
  362. service_status.dwCurrentState = SERVICE_RUNNING;
  363. SetServiceStatus(service_handle, &service_status);
  364. return 0;
  365. }
  366. /* Stop the service */
  367. int stop_service(unsigned long exitcode, bool graceful, bool default_action) {
  368. allow_restart = false;
  369. if (wait_handle) UnregisterWait(wait_handle);
  370. if (default_action && ! exitcode && ! graceful) {
  371. log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_GRACEFUL_SUICIDE, service_name, 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);
  372. graceful = true;
  373. }
  374. /* Signal we are stopping */
  375. if (graceful) {
  376. service_status.dwCurrentState = SERVICE_STOP_PENDING;
  377. service_status.dwWaitHint = NSSM_WAITHINT_MARGIN;
  378. if (stop_method & NSSM_STOP_METHOD_CONSOLE && imports.AttachConsole) service_status.dwWaitHint += kill_console_delay;
  379. if (stop_method & NSSM_STOP_METHOD_WINDOW) service_status.dwWaitHint += kill_window_delay;
  380. if (stop_method & NSSM_STOP_METHOD_THREADS) service_status.dwWaitHint += kill_threads_delay;
  381. SetServiceStatus(service_handle, &service_status);
  382. }
  383. /* Nothing to do if service isn't running */
  384. if (pid) {
  385. /* Shut down service */
  386. log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service_name, exe, 0);
  387. kill_process(service_name, stop_method, process_handle, pid, 0);
  388. }
  389. else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service_name, exe, 0);
  390. end_service((void *) pid, true);
  391. /* Signal we stopped */
  392. if (graceful) {
  393. service_status.dwCurrentState = SERVICE_STOPPED;
  394. if (exitcode) {
  395. service_status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
  396. service_status.dwServiceSpecificExitCode = exitcode;
  397. }
  398. else {
  399. service_status.dwWin32ExitCode = NO_ERROR;
  400. service_status.dwServiceSpecificExitCode = 0;
  401. }
  402. SetServiceStatus(service_handle, &service_status);
  403. }
  404. return exitcode;
  405. }
  406. /* Callback function triggered when the server exits */
  407. void CALLBACK end_service(void *arg, unsigned char why) {
  408. if (stopping) return;
  409. stopping = true;
  410. pid = (unsigned long) arg;
  411. /* Check exit code */
  412. unsigned long exitcode = 0;
  413. char code[16];
  414. FILETIME exit_time;
  415. GetExitCodeProcess(process_handle, &exitcode);
  416. if (exitcode == STILL_ACTIVE || get_process_exit_time(process_handle, &exit_time)) GetSystemTimeAsFileTime(&exit_time);
  417. CloseHandle(process_handle);
  418. /*
  419. Log that the service ended BEFORE logging about killing the process
  420. tree. See below for the possible values of the why argument.
  421. */
  422. if (! why) {
  423. _snprintf_s(code, sizeof(code), _TRUNCATE, "%lu", exitcode);
  424. log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, exe, service_name, code, 0);
  425. }
  426. /* Clean up. */
  427. if (exitcode == STILL_ACTIVE) exitcode = 0;
  428. kill_process_tree(service_name, stop_method, pid, exitcode, pid, &creation_time, &exit_time);
  429. /*
  430. The why argument is true if our wait timed out or false otherwise.
  431. Our wait is infinite so why will never be true when called by the system.
  432. If it is indeed true, assume we were called from stop_service() because
  433. this is a controlled shutdown, and don't take any restart action.
  434. */
  435. if (why) return;
  436. if (! allow_restart) return;
  437. /* What action should we take? */
  438. int action = NSSM_EXIT_RESTART;
  439. unsigned char action_string[ACTION_LEN];
  440. bool default_action;
  441. if (! get_exit_action(service_name, &exitcode, action_string, &default_action)) {
  442. for (int i = 0; exit_action_strings[i]; i++) {
  443. if (! _strnicmp((const char *) action_string, exit_action_strings[i], ACTION_LEN)) {
  444. action = i;
  445. break;
  446. }
  447. }
  448. }
  449. process_handle = 0;
  450. pid = 0;
  451. switch (action) {
  452. /* Try to restart the service or return failure code to service manager */
  453. case NSSM_EXIT_RESTART:
  454. log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service_name, code, exit_action_strings[action], exe, 0);
  455. while (monitor_service()) {
  456. log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, exe, service_name, 0);
  457. Sleep(30000);
  458. }
  459. break;
  460. /* Do nothing, just like srvany would */
  461. case NSSM_EXIT_IGNORE:
  462. log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service_name, code, exit_action_strings[action], exe, 0);
  463. Sleep(INFINITE);
  464. break;
  465. /* Tell the service manager we are finished */
  466. case NSSM_EXIT_REALLY:
  467. log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service_name, code, exit_action_strings[action], 0);
  468. stop_service(exitcode, true, default_action);
  469. break;
  470. /* Fake a crash so pre-Vista service managers will run recovery actions. */
  471. case NSSM_EXIT_UNCLEAN:
  472. log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service_name, code, exit_action_strings[action], 0);
  473. stop_service(exitcode, false, default_action);
  474. free_imports();
  475. exit(exitcode);
  476. break;
  477. }
  478. }
  479. void throttle_restart() {
  480. /* This can't be a restart if the service is already running. */
  481. if (! throttle++) return;
  482. int ms = throttle_milliseconds();
  483. if (throttle > 7) throttle = 8;
  484. char threshold[8], milliseconds[8];
  485. _snprintf_s(threshold, sizeof(threshold), _TRUNCATE, "%lu", throttle_delay);
  486. _snprintf_s(milliseconds, sizeof(milliseconds), _TRUNCATE, "%lu", ms);
  487. log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service_name, threshold, milliseconds, 0);
  488. if (use_critical_section) EnterCriticalSection(&throttle_section);
  489. else if (throttle_timer) {
  490. ZeroMemory(&throttle_duetime, sizeof(throttle_duetime));
  491. throttle_duetime.QuadPart = 0 - (ms * 10000LL);
  492. SetWaitableTimer(throttle_timer, &throttle_duetime, 0, 0, 0, 0);
  493. }
  494. service_status.dwCurrentState = SERVICE_PAUSED;
  495. SetServiceStatus(service_handle, &service_status);
  496. if (use_critical_section) {
  497. imports.SleepConditionVariableCS(&throttle_condition, &throttle_section, ms);
  498. LeaveCriticalSection(&throttle_section);
  499. }
  500. else {
  501. if (throttle_timer) WaitForSingleObject(throttle_timer, INFINITE);
  502. else Sleep(ms);
  503. }
  504. }
  505. /*
  506. When responding to a stop (or any other) request we need to set dwWaitHint to
  507. the number of milliseconds we expect the operation to take, and optionally
  508. increase dwCheckPoint. If dwWaitHint milliseconds elapses without the
  509. operation completing or dwCheckPoint increasing, the system will consider the
  510. service to be hung.
  511. However the system will consider the service to be hung after 30000
  512. milliseconds regardless of the value of dwWaitHint if dwCheckPoint has not
  513. changed. Therefore if we want to wait longer than that we must periodically
  514. increase dwCheckPoint.
  515. Furthermore, it will consider the service to be hung after 60000 milliseconds
  516. regardless of the value of dwCheckPoint unless dwWaitHint is increased every
  517. time dwCheckPoint is also increased.
  518. Our strategy then is to retrieve the initial dwWaitHint and wait for
  519. NSSM_SHUTDOWN_CHECKPOINT milliseconds. If the process is still running and
  520. we haven't finished waiting we increment dwCheckPoint and add whichever is
  521. smaller of NSSM_SHUTDOWN_CHECKPOINT or the remaining timeout to dwWaitHint.
  522. Only doing both these things will prevent the system from killing the service.
  523. Returns: 1 if the wait timed out.
  524. 0 if the wait completed.
  525. -1 on error.
  526. */
  527. int await_shutdown(char *function_name, char *service_name, SERVICE_STATUS_HANDLE service_handle, SERVICE_STATUS *service_status, HANDLE process_handle, unsigned long timeout) {
  528. unsigned long interval;
  529. unsigned long waithint;
  530. unsigned long ret;
  531. unsigned long waited;
  532. char interval_milliseconds[16];
  533. char timeout_milliseconds[16];
  534. char waited_milliseconds[16];
  535. char *function = function_name;
  536. /* Add brackets to function name. */
  537. size_t funclen = strlen(function_name) + 3;
  538. char *func = (char *) HeapAlloc(GetProcessHeap(), 0, funclen);
  539. if (func) {
  540. if (_snprintf_s(func, funclen, _TRUNCATE, "%s()", function_name) > -1) function = func;
  541. }
  542. _snprintf_s(timeout_milliseconds, sizeof(timeout_milliseconds), _TRUNCATE, "%lu", timeout);
  543. waithint = service_status->dwWaitHint;
  544. waited = 0;
  545. while (waited < timeout) {
  546. interval = timeout - waited;
  547. if (interval > NSSM_SHUTDOWN_CHECKPOINT) interval = NSSM_SHUTDOWN_CHECKPOINT;
  548. service_status->dwCurrentState = SERVICE_STOP_PENDING;
  549. service_status->dwWaitHint += interval;
  550. service_status->dwCheckPoint++;
  551. SetServiceStatus(service_handle, service_status);
  552. if (waited) {
  553. _snprintf_s(waited_milliseconds, sizeof(waited_milliseconds), _TRUNCATE, "%lu", waited);
  554. _snprintf_s(interval_milliseconds, sizeof(interval_milliseconds), _TRUNCATE, "%lu", interval);
  555. log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_AWAITING_SHUTDOWN, function, service_name, waited_milliseconds, interval_milliseconds, timeout_milliseconds, 0);
  556. }
  557. switch (WaitForSingleObject(process_handle, interval)) {
  558. case WAIT_OBJECT_0:
  559. ret = 0;
  560. goto awaited;
  561. case WAIT_TIMEOUT:
  562. ret = 1;
  563. break;
  564. default:
  565. ret = -1;
  566. goto awaited;
  567. }
  568. waited += interval;
  569. }
  570. awaited:
  571. if (func) HeapFree(GetProcessHeap(), 0, func);
  572. return ret;
  573. }