service.cpp 8.5 KB

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