service.cpp 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. #include "nssm.h"
  2. SERVICE_STATUS service_status;
  3. SERVICE_STATUS_HANDLE service_handle;
  4. HANDLE wait_handle;
  5. HANDLE pid;
  6. static char service_name[MAX_PATH];
  7. char exe[MAX_PATH];
  8. char flags[MAX_PATH];
  9. char dir[MAX_PATH];
  10. static enum { NSSM_EXIT_RESTART, NSSM_EXIT_IGNORE, NSSM_EXIT_REALLY } exit_actions;
  11. static const char *exit_action_strings[] = { "Restart", "Ignore", "Exit", 0 };
  12. /* Connect to the service manager */
  13. SC_HANDLE open_service_manager() {
  14. SC_HANDLE ret = OpenSCManager(0, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS);
  15. if (! ret) {
  16. eventprintf(EVENTLOG_ERROR_TYPE, "Unable to connect to service manager!\nPerhaps you need to be an administrator...");
  17. return 0;
  18. }
  19. return ret;
  20. }
  21. /* About to install the service */
  22. int pre_install_service(int argc, char **argv) {
  23. /* Show the dialogue box if we didn't give the */
  24. if (argc < 2) return nssm_gui(IDD_INSTALL, argv[0]);
  25. /* Arguments are optional */
  26. char *flags;
  27. if (argc == 2) flags = "";
  28. else flags = argv[2];
  29. return install_service(argv[0], argv[1], flags);
  30. }
  31. /* About to remove the service */
  32. int pre_remove_service(int argc, char **argv) {
  33. /* Show dialogue box if we didn't pass service name and "confirm" */
  34. if (argc < 2) return nssm_gui(IDD_REMOVE, argv[0]);
  35. if (str_equiv(argv[1], "confirm")) return remove_service(argv[0]);
  36. fprintf(stderr, "To remove a service without confirmation: nssm remove <servicename> confirm\n");
  37. return 100;
  38. }
  39. /* Install the service */
  40. int install_service(char *name, char *exe, char *flags) {
  41. /* Open service manager */
  42. SC_HANDLE services = open_service_manager();
  43. if (! services) {
  44. fprintf(stderr, "Error opening service manager!\n");
  45. return 2;
  46. }
  47. /* Get path of this program */
  48. char path[MAX_PATH];
  49. GetModuleFileName(0, path, MAX_PATH);
  50. /* Construct command */
  51. char command[MAX_PATH];
  52. size_t runlen = strlen(NSSM_RUN);
  53. size_t pathlen = strlen(path);
  54. if (pathlen + runlen + 2 >= MAX_PATH) {
  55. fprintf(stderr, "The full path to " NSSM " is too long!\n");
  56. return 3;
  57. }
  58. if (snprintf(command, sizeof(command), "\"%s\" %s", path, NSSM_RUN) < 0) {
  59. fprintf(stderr, "Out of memory for ImagePath!\n");
  60. return 4;
  61. }
  62. /* Work out directory name */
  63. size_t len = strlen(exe);
  64. size_t i;
  65. for (i = len; i && exe[i] != '\\' && exe[i] != '/'; i--);
  66. char dir[MAX_PATH];
  67. memmove(dir, exe, i);
  68. dir[i] = '\0';
  69. /* Create the service */
  70. 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);
  71. if (! service) {
  72. fprintf(stderr, "Error creating service!\n");
  73. CloseServiceHandle(services);
  74. return 5;
  75. }
  76. /* Now we need to put the parameters into the registry */
  77. if (create_parameters(name, exe, flags, dir)) {
  78. fprintf(stderr, "Error setting startup parameters for the service!\n");
  79. DeleteService(service);
  80. CloseServiceHandle(services);
  81. return 6;
  82. }
  83. /* Cleanup */
  84. CloseServiceHandle(service);
  85. CloseServiceHandle(services);
  86. printf("Service \"%s\" installed successfully!\n", name);
  87. return 0;
  88. }
  89. /* Remove the service */
  90. int remove_service(char *name) {
  91. /* Open service manager */
  92. SC_HANDLE services = open_service_manager();
  93. if (! services) {
  94. fprintf(stderr, "Error opening service manager!\n");
  95. return 2;
  96. }
  97. /* Try to open the service */
  98. SC_HANDLE service = OpenService(services, name, SC_MANAGER_ALL_ACCESS);
  99. if (! service) {
  100. fprintf(stderr, "Can't open service!");
  101. CloseServiceHandle(services);
  102. return 3;
  103. }
  104. /* Try to delete the service */
  105. if (! DeleteService(service)) {
  106. fprintf(stderr, "Error deleting service!\n");
  107. CloseServiceHandle(service);
  108. CloseServiceHandle(services);
  109. return 4;
  110. }
  111. /* Cleanup */
  112. CloseServiceHandle(service);
  113. CloseServiceHandle(services);
  114. printf("Service \"%s\" removed successfully!\n", name);
  115. return 0;
  116. }
  117. /* Service initialisation */
  118. void WINAPI service_main(unsigned long argc, char **argv) {
  119. if (_snprintf(service_name, sizeof(service_name), "%s", argv[0]) < 0) {
  120. eventprintf(EVENTLOG_ERROR_TYPE, "service_main(): Out of memory for service_name!");
  121. return;
  122. }
  123. /* Initialise status */
  124. ZeroMemory(&service_status, sizeof(service_status));
  125. service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
  126. service_status.dwCurrentState = SERVICE_RUNNING;
  127. service_status.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP;
  128. service_status.dwWin32ExitCode = NO_ERROR;
  129. service_status.dwServiceSpecificExitCode = 0;
  130. service_status.dwCheckPoint = 0;
  131. service_status.dwWaitHint = 1000;
  132. /* Signal we AREN'T running the server */
  133. pid = 0;
  134. /* Get startup parameters */
  135. int ret = get_parameters(argv[0], exe, sizeof(exe), flags, sizeof(flags), dir, sizeof(dir));
  136. if (ret) {
  137. eventprintf(EVENTLOG_ERROR_TYPE, "service_main(): Can't get startup parameters: error %d", ret);
  138. return;
  139. }
  140. /* Register control handler */
  141. service_handle = RegisterServiceCtrlHandlerEx(NSSM, service_control_handler, 0);
  142. if (! service_handle) {
  143. eventprintf(EVENTLOG_ERROR_TYPE, "service_main(): RegisterServiceCtrlHandlerEx() failed: %s", error_string(GetLastError()));
  144. return;
  145. }
  146. /* Try to create the exit action parameters; we don't care if it fails */
  147. create_exit_action(argv[0], exit_action_strings[0]);
  148. monitor_service();
  149. }
  150. int monitor_service() {
  151. /* Set service status to started */
  152. int ret = start_service();
  153. if (ret) {
  154. eventprintf(EVENTLOG_ERROR_TYPE, "Can't start service %s: error code %d", service_name, ret);
  155. return ret;
  156. }
  157. eventprintf(EVENTLOG_INFORMATION_TYPE, "Started process %s %s in %s for service %s", exe, flags, dir, service_name);
  158. /* Monitor service service */
  159. if (! RegisterWaitForSingleObject(&wait_handle, pid, end_service, 0, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {
  160. eventprintf(EVENTLOG_WARNING_TYPE, "RegisterWaitForSingleObject() returned %s - service may claim to be still running when %s exits ", error_string(GetLastError()), exe);
  161. }
  162. return 0;
  163. }
  164. /* Service control handler */
  165. unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {
  166. switch (control) {
  167. case SERVICE_CONTROL_SHUTDOWN:
  168. case SERVICE_CONTROL_STOP:
  169. stop_service(0);
  170. return NO_ERROR;
  171. }
  172. /* Unknown control */
  173. return ERROR_CALL_NOT_IMPLEMENTED;
  174. }
  175. /* Start the service */
  176. int start_service() {
  177. if (pid) return 0;
  178. /* Allocate a STARTUPINFO structure for a new process */
  179. STARTUPINFO si;
  180. ZeroMemory(&si, sizeof(si));
  181. si.cb = sizeof(si);
  182. /* Allocate a PROCESSINFO structure for the process */
  183. PROCESS_INFORMATION pi;
  184. ZeroMemory(&pi, sizeof(pi));
  185. /* Launch executable with arguments */
  186. char cmd[MAX_PATH];
  187. if (_snprintf(cmd, sizeof(cmd), "%s %s", exe, flags) < 0) {
  188. eventprintf(EVENTLOG_ERROR_TYPE, "Error constructing command line");
  189. return stop_service(2);
  190. }
  191. if (! CreateProcess(0, cmd, 0, 0, 0, 0, 0, dir, &si, &pi)) {
  192. eventprintf(EVENTLOG_ERROR_TYPE, "Can't launch %s. CreateProcess() returned %s", exe, error_string(GetLastError()));
  193. return stop_service(3);
  194. }
  195. pid = pi.hProcess;
  196. /* Signal successful start */
  197. service_status.dwCurrentState = SERVICE_RUNNING;
  198. SetServiceStatus(service_handle, &service_status);
  199. return 0;
  200. }
  201. /* Stop the service */
  202. int stop_service(unsigned long exitcode) {
  203. /* Signal we are stopping */
  204. service_status.dwCurrentState = SERVICE_STOP_PENDING;
  205. SetServiceStatus(service_handle, &service_status);
  206. /* Nothing to do if server isn't running */
  207. if (pid) {
  208. /* Shut down server */
  209. TerminateProcess(pid, 0);
  210. pid = 0;
  211. }
  212. /* Signal we stopped */
  213. service_status.dwCurrentState = SERVICE_STOPPED;
  214. if (exitcode) {
  215. service_status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
  216. service_status.dwServiceSpecificExitCode = exitcode;
  217. }
  218. else {
  219. service_status.dwWin32ExitCode = NO_ERROR;
  220. service_status.dwServiceSpecificExitCode = 0;
  221. }
  222. SetServiceStatus(service_handle, &service_status);
  223. return exitcode;
  224. }
  225. /* Callback function triggered when the server exits */
  226. void CALLBACK end_service(void *arg, unsigned char why) {
  227. /* Check exit code */
  228. unsigned long ret = 0;
  229. GetExitCodeProcess(pid, &ret);
  230. eventprintf(EVENTLOG_INFORMATION_TYPE, "Process %s for service %s exited with return code %u", exe, service_name, ret);
  231. /* What action should we take? */
  232. int action = NSSM_EXIT_RESTART;
  233. unsigned char action_string[ACTION_LEN];
  234. if (! get_exit_action(service_name, &ret, action_string)) {
  235. for (int i = 0; exit_action_strings[i]; i++) {
  236. if (! _strnicmp((const char *) action_string, exit_action_strings[i], ACTION_LEN)) {
  237. action = i;
  238. break;
  239. }
  240. }
  241. }
  242. pid = 0;
  243. switch (action) {
  244. /* Try to restart the service or return failure code to service manager */
  245. case NSSM_EXIT_RESTART:
  246. eventprintf(EVENTLOG_INFORMATION_TYPE, "Action for exit code %lu is %s: Attempting to restart %s for service %s", ret, exit_action_strings[action], exe, service_name);
  247. while (monitor_service()) {
  248. eventprintf(EVENTLOG_INFORMATION_TYPE, "Failed to restart %s - sleeping ", exe, ret);
  249. Sleep(30000);
  250. }
  251. break;
  252. /* Do nothing, just like srvany would */
  253. case NSSM_EXIT_IGNORE:
  254. eventprintf(EVENTLOG_INFORMATION_TYPE, "Action for exit code %lu is %s: Not attempting to restart %s for service %s", ret, exit_action_strings[action], exe, service_name);
  255. Sleep(INFINITE);
  256. break;
  257. /* Tell the service manager we are finished */
  258. case NSSM_EXIT_REALLY:
  259. eventprintf(EVENTLOG_INFORMATION_TYPE, "Action for exit code %lu is %s: Stopping service %s", ret, exit_action_strings[action], service_name);
  260. stop_service(ret);
  261. break;
  262. }
  263. }