service.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  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. log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENSCMANAGER_FAILED, 0);
  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. log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "service_name", "service_main()", 0);
  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. log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, argv[0], 0);
  138. return;
  139. }
  140. /* Register control handler */
  141. service_handle = RegisterServiceCtrlHandlerEx(NSSM, service_control_handler, 0);
  142. if (! service_handle) {
  143. log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_REGISTERSERVICECTRLHANDER_FAILED, GetLastError(), 0);
  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. char code[16];
  155. snprintf(code, sizeof(code), "%d", ret);
  156. log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, exe, service_name, ret, 0);
  157. return ret;
  158. }
  159. log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, exe, flags, service_name, dir, 0);
  160. /* Monitor service service */
  161. if (! RegisterWaitForSingleObject(&wait_handle, pid, end_service, 0, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {
  162. log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service_name, exe, GetLastError(), 0);
  163. }
  164. return 0;
  165. }
  166. /* Service control handler */
  167. unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {
  168. switch (control) {
  169. case SERVICE_CONTROL_SHUTDOWN:
  170. case SERVICE_CONTROL_STOP:
  171. stop_service(0);
  172. return NO_ERROR;
  173. }
  174. /* Unknown control */
  175. return ERROR_CALL_NOT_IMPLEMENTED;
  176. }
  177. /* Start the service */
  178. int start_service() {
  179. if (pid) return 0;
  180. /* Allocate a STARTUPINFO structure for a new process */
  181. STARTUPINFO si;
  182. ZeroMemory(&si, sizeof(si));
  183. si.cb = sizeof(si);
  184. /* Allocate a PROCESSINFO structure for the process */
  185. PROCESS_INFORMATION pi;
  186. ZeroMemory(&pi, sizeof(pi));
  187. /* Launch executable with arguments */
  188. char cmd[MAX_PATH];
  189. if (_snprintf(cmd, sizeof(cmd), "%s %s", exe, flags) < 0) {
  190. log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "command line", "start_service", 0);
  191. return stop_service(2);
  192. }
  193. if (! CreateProcess(0, cmd, 0, 0, 0, 0, 0, dir, &si, &pi)) {
  194. log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service_name, exe, GetLastError(), 0);
  195. return stop_service(3);
  196. }
  197. pid = pi.hProcess;
  198. /* Signal successful start */
  199. service_status.dwCurrentState = SERVICE_RUNNING;
  200. SetServiceStatus(service_handle, &service_status);
  201. return 0;
  202. }
  203. /* Stop the service */
  204. int stop_service(unsigned long exitcode) {
  205. /* Signal we are stopping */
  206. service_status.dwCurrentState = SERVICE_STOP_PENDING;
  207. SetServiceStatus(service_handle, &service_status);
  208. /* Nothing to do if server isn't running */
  209. if (pid) {
  210. /* Shut down server */
  211. log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service_name, exe, 0);
  212. TerminateProcess(pid, 0);
  213. pid = 0;
  214. }
  215. else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service_name, exe, 0);
  216. /* Signal we stopped */
  217. service_status.dwCurrentState = SERVICE_STOPPED;
  218. if (exitcode) {
  219. service_status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
  220. service_status.dwServiceSpecificExitCode = exitcode;
  221. }
  222. else {
  223. service_status.dwWin32ExitCode = NO_ERROR;
  224. service_status.dwServiceSpecificExitCode = 0;
  225. }
  226. SetServiceStatus(service_handle, &service_status);
  227. return exitcode;
  228. }
  229. /* Callback function triggered when the server exits */
  230. void CALLBACK end_service(void *arg, unsigned char why) {
  231. /* Check exit code */
  232. unsigned long ret = 0;
  233. GetExitCodeProcess(pid, &ret);
  234. char code[16];
  235. _snprintf(code, sizeof(code), "%d", ret);
  236. log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, exe, service_name, code, 0);
  237. /* What action should we take? */
  238. int action = NSSM_EXIT_RESTART;
  239. unsigned char action_string[ACTION_LEN];
  240. if (! get_exit_action(service_name, &ret, action_string)) {
  241. for (int i = 0; exit_action_strings[i]; i++) {
  242. if (! _strnicmp((const char *) action_string, exit_action_strings[i], ACTION_LEN)) {
  243. action = i;
  244. break;
  245. }
  246. }
  247. }
  248. pid = 0;
  249. switch (action) {
  250. /* Try to restart the service or return failure code to service manager */
  251. case NSSM_EXIT_RESTART:
  252. log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service_name, code, exit_action_strings[action], exe, 0);
  253. while (monitor_service()) {
  254. log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, exe, service_name, 0);
  255. Sleep(30000);
  256. }
  257. break;
  258. /* Do nothing, just like srvany would */
  259. case NSSM_EXIT_IGNORE:
  260. log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service_name, code, exit_action_strings[action], exe, 0);
  261. Sleep(INFINITE);
  262. break;
  263. /* Tell the service manager we are finished */
  264. case NSSM_EXIT_REALLY:
  265. log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service_name, code, exit_action_strings[action], 0);
  266. stop_service(ret);
  267. break;
  268. }
  269. }