service.cpp 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  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. /* Install the service */
  19. int install_service(char *name) {
  20. #ifdef GUI
  21. /* Show the dialogue box */
  22. return nssm_gui(IDD_INSTALL, name);
  23. #else
  24. fprintf(stderr, "Unimplemented\n");
  25. return 1;
  26. #endif
  27. }
  28. /* Remove the service */
  29. int remove_service(char *name) {
  30. #ifdef GUI
  31. return nssm_gui(IDD_REMOVE, name);
  32. #else
  33. fprintf(stderr, "Unimplemented\n");
  34. return 1;
  35. #endif
  36. }
  37. /* Service initialisation */
  38. void WINAPI service_main(unsigned long argc, char **argv) {
  39. /* Initialise status */
  40. ZeroMemory(&service_status, sizeof(service_status));
  41. service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
  42. service_status.dwCurrentState = SERVICE_RUNNING;
  43. service_status.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP;
  44. service_status.dwWin32ExitCode = NO_ERROR;
  45. service_status.dwServiceSpecificExitCode = 0;
  46. service_status.dwCheckPoint = 0;
  47. service_status.dwWaitHint = 1000;
  48. /* Signal we AREN'T running the server */
  49. pid = 0;
  50. /* Get startup parameters */
  51. int ret = get_parameters(argv[0], exe, sizeof(exe), flags, sizeof(flags), dir, sizeof(dir));
  52. if (ret) {
  53. eventprintf(EVENTLOG_ERROR_TYPE, "service_main(): Can't get startup parameters: error %d", ret);
  54. return;
  55. }
  56. /* Register control handler */
  57. service_handle = RegisterServiceCtrlHandlerEx(NSSM, service_control_handler, 0);
  58. if (! service_handle) {
  59. eventprintf(EVENTLOG_ERROR_TYPE, "service_main(): RegisterServiceCtrlHandlerEx() failed: %s", error_string(GetLastError()));
  60. return;
  61. }
  62. monitor_service();
  63. }
  64. int monitor_service() {
  65. /* Set service status to started */
  66. int ret = start_service();
  67. if (ret) {
  68. eventprintf(EVENTLOG_ERROR_TYPE, "Can't start service: error code %d", ret);
  69. return ret;
  70. }
  71. eventprintf(EVENTLOG_INFORMATION_TYPE, "Started process %s %s in %s", exe, flags, dir);
  72. /* Monitor service service */
  73. if (! RegisterWaitForSingleObject(&wait_handle, pid, end_service, 0, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {
  74. eventprintf(EVENTLOG_WARNING_TYPE, "RegisterWaitForSingleObject() returned %s - service may claim to be still running when %s exits ", error_string(GetLastError()), exe);
  75. }
  76. return 0;
  77. }
  78. /* Service control handler */
  79. unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {
  80. switch (control) {
  81. case SERVICE_CONTROL_SHUTDOWN:
  82. case SERVICE_CONTROL_STOP:
  83. stop_service(0);
  84. return NO_ERROR;
  85. }
  86. /* Unknown control */
  87. return ERROR_CALL_NOT_IMPLEMENTED;
  88. }
  89. /* Start the service */
  90. int start_service() {
  91. if (pid) return 0;
  92. /* Allocate a STARTUPINFO structure for a new process */
  93. STARTUPINFO si;
  94. ZeroMemory(&si, sizeof(si));
  95. si.cb = sizeof(si);
  96. /* Allocate a PROCESSINFO structure for the process */
  97. PROCESS_INFORMATION pi;
  98. ZeroMemory(&pi, sizeof(pi));
  99. /* Launch executable with arguments */
  100. char cmd[MAX_PATH];
  101. if (_snprintf(cmd, sizeof(cmd), "%s %s", exe, flags) < 0) {
  102. eventprintf(EVENTLOG_ERROR_TYPE, "Error constructing command line");
  103. return stop_service(2);
  104. }
  105. if (! CreateProcess(0, cmd, 0, 0, 0, 0, 0, dir, &si, &pi)) {
  106. eventprintf(EVENTLOG_ERROR_TYPE, "Can't launch %s. CreateProcess() returned %s", exe, error_string(GetLastError()));
  107. return stop_service(3);
  108. }
  109. pid = pi.hProcess;
  110. /* Signal successful start */
  111. service_status.dwCurrentState = SERVICE_RUNNING;
  112. SetServiceStatus(service_handle, &service_status);
  113. return 0;
  114. }
  115. /* Stop the service */
  116. int stop_service(unsigned long exitcode) {
  117. /* Signal we are stopping */
  118. service_status.dwCurrentState = SERVICE_STOP_PENDING;
  119. SetServiceStatus(service_handle, &service_status);
  120. /* Nothing to do if server isn't running */
  121. if (pid) {
  122. /* Shut down server */
  123. TerminateProcess(pid, 0);
  124. pid = 0;
  125. }
  126. /* Signal we stopped */
  127. service_status.dwCurrentState = SERVICE_STOPPED;
  128. if (exitcode) {
  129. service_status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
  130. service_status.dwServiceSpecificExitCode = exitcode;
  131. }
  132. else {
  133. service_status.dwWin32ExitCode = NO_ERROR;
  134. service_status.dwServiceSpecificExitCode = 0;
  135. }
  136. SetServiceStatus(service_handle, &service_status);
  137. return exitcode;
  138. }
  139. /* Callback function triggered when the server exits */
  140. void CALLBACK end_service(void *arg, unsigned char why) {
  141. /* Check exit code */
  142. unsigned long ret = 0;
  143. GetExitCodeProcess(pid, &ret);
  144. /* Force an error code if none given, so system can restart this service */
  145. /*if (! ret) {
  146. eventprintf(EVENTLOG_INFORMATION_TYPE, "Process exited with return code 0 - overriding with return code 111 so the service manager will notice the failure");
  147. ret = 111;
  148. }
  149. else */eventprintf(EVENTLOG_INFORMATION_TYPE, "Process %s exited with return code %u", exe, ret);
  150. /* Try to restart the service or return failure code to service manager */
  151. pid = 0;
  152. while (monitor_service()) {
  153. eventprintf(EVENTLOG_INFORMATION_TYPE, "Failed to restart %s - sleeping ", exe, ret);
  154. Sleep(30000);
  155. }
  156. }