void RunAsServiceIfNeeded()

in minifi_main/MiNiFiWindowsService.cpp [51:247]


void RunAsServiceIfNeeded() {
  static const int WAIT_TIME_EXE_TERMINATION = 5000;
  static const int WAIT_TIME_EXE_RESTART = 60000;

  static SERVICE_STATUS s_serviceStatus;
  static SERVICE_STATUS_HANDLE s_statusHandle;
  static HANDLE s_hProcess;
  static HANDLE s_hEvent;

  static char serviceNameMutable[] = SERVICE_NAME;

  static auto Log = []() {
    static std::shared_ptr<minifi::core::logging::Logger> s_logger = minifi::core::logging::LoggerConfiguration::getConfiguration().getLogger("service");
    return s_logger;
  };

  SERVICE_TABLE_ENTRY serviceTable[] = {
    {
      serviceNameMutable,
      [](DWORD argc, LPTSTR *argv) {
        setSyslogLogger();

        Log()->log_trace("ServiceCtrlDispatcher");

        s_hEvent = CreateEvent(0, TRUE, FALSE, SERVICE_TERMINATION_EVENT_NAME);
        if (!s_hEvent) {
          Log()->log_error("!CreateEvent lastError {:#x}", GetLastError());
          return;
        }

        s_statusHandle = RegisterServiceCtrlHandler(
          SERVICE_NAME,
          [](DWORD ctrlCode) {
            Log()->log_trace("ServiceCtrlHandler ctrlCode {}", ctrlCode);

            if (SERVICE_CONTROL_STOP == ctrlCode) {
              Log()->log_trace("ServiceCtrlHandler ctrlCode = SERVICE_CONTROL_STOP");

              // Set service status SERVICE_STOP_PENDING.
              s_serviceStatus.dwControlsAccepted = 0;
              s_serviceStatus.dwCurrentState = SERVICE_STOP_PENDING;
              s_serviceStatus.dwWin32ExitCode = 0;

              if (!SetServiceStatus(s_statusHandle, &s_serviceStatus)) {
                Log()->log_error("!SetServiceStatus SERVICE_STOP_PENDING lastError {:#x}", GetLastError());
              }

              bool exeTerminated = false;

              SetEvent(s_hEvent);

              Log()->log_info("Wait for exe termination");
              switch (auto res = WaitForSingleObject(s_hProcess, WAIT_TIME_EXE_TERMINATION)) {
                case WAIT_OBJECT_0:
                  Log()->log_info("Exe terminated");
                  exeTerminated = true;
                  break;

                case WAIT_TIMEOUT:
                  Log()->log_error("WaitForSingleObject timeout {}", WAIT_TIME_EXE_TERMINATION);
                  break;

                default:
                  Log()->log_error("!WaitForSingleObject return {}", res);
              }

              if (!exeTerminated) {
                Log()->log_info("TerminateProcess");
                if (TerminateProcess(s_hProcess, 0)) {
                  s_serviceStatus.dwControlsAccepted = 0;
                  s_serviceStatus.dwCurrentState = SERVICE_STOPPED;
                  s_serviceStatus.dwWin32ExitCode = 0;

                  if (!SetServiceStatus(s_statusHandle, &s_serviceStatus)) {
                    Log()->log_error("!SetServiceStatus SERVICE_STOPPED lastError {:#x}", GetLastError());
                  }
                } else {
                  Log()->log_error("!TerminateProcess lastError {:#x}", GetLastError());
                }
              }
            }
          }
        );

        if (!s_statusHandle) {
          Log()->log_error("!RegisterServiceCtrlHandler lastError {:#x}", GetLastError());
          return;
        }

        // Set service status SERVICE_START_PENDING.
        ZeroMemory(&s_serviceStatus, sizeof(s_serviceStatus));
        s_serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
        s_serviceStatus.dwControlsAccepted = 0;
        s_serviceStatus.dwCurrentState = SERVICE_START_PENDING;
        s_serviceStatus.dwWin32ExitCode = 0;
        s_serviceStatus.dwServiceSpecificExitCode = 0;

        if (!SetServiceStatus(s_statusHandle, &s_serviceStatus)) {
          Log()->log_error("!SetServiceStatus SERVICE_START_PENDING lastError {:#x}", GetLastError());
          return;
        }

        // Set service status SERVICE_RUNNING.
        s_serviceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
        s_serviceStatus.dwCurrentState = SERVICE_RUNNING;
        s_serviceStatus.dwWin32ExitCode = 0;

        if (!SetServiceStatus(s_statusHandle, &s_serviceStatus)) {
          Log()->log_error("!SetServiceStatus SERVICE_RUNNING lastError {:#x}", GetLastError());

          // Set service status SERVICE_START_PENDING.
          s_serviceStatus.dwControlsAccepted = 0;
          s_serviceStatus.dwCurrentState = SERVICE_STOPPED;
          s_serviceStatus.dwWin32ExitCode = GetLastError();

          if (!SetServiceStatus(s_statusHandle, &s_serviceStatus)) {
            Log()->log_error("!SetServiceStatus SERVICE_STOPPED lastError {:#x}", GetLastError());
          }

          return;
        }

        char filePath[MAX_PATH];
        if (!GetModuleFileName(0, filePath, _countof(filePath))) {
          Log()->log_error("!GetModuleFileName lastError {:#x}", GetLastError());
          return;
        }

        do {
          Log()->log_debug("Start exe path {}", filePath);

          STARTUPINFO startupInfo{};
          startupInfo.cb = sizeof(startupInfo);

          PROCESS_INFORMATION processInformation{};

          if (!CreateProcess(filePath, 0, 0, 0, 0, FALSE, 0, 0, &startupInfo, &processInformation)) {
            Log()->log_error("!CreateProcess lastError {:#x}", GetLastError());
            return;
          }

          s_hProcess = processInformation.hProcess;

          Log()->log_info("{} started", filePath);

          auto res = WaitForSingleObject(processInformation.hProcess, INFINITE);
          CloseHandle(processInformation.hProcess);
          CloseHandle(processInformation.hThread);

          if (WAIT_FAILED == res) {
            Log()->log_error("!WaitForSingleObject hProcess lastError {:#x}", GetLastError());
          } else if (WAIT_OBJECT_0 != res) {
            Log()->log_error("!WaitForSingleObject hProcess return {}", res);
          }

          Log()->log_info("Sleep {} sec before restarting exe", WAIT_TIME_EXE_RESTART/1000);
          res = WaitForSingleObject(s_hEvent, WAIT_TIME_EXE_RESTART);

          if (WAIT_OBJECT_0 == res) {
            Log()->log_info("Service was stopped, exe won't be restarted");
            break;
          }

          if (WAIT_FAILED == res) {
            Log()->log_error("!WaitForSingleObject s_hEvent lastError {:#x}", GetLastError());
          } if (WAIT_TIMEOUT != res) {
            Log()->log_error("!WaitForSingleObject s_hEvent return {}", res);
          }
        } while (true);

        s_serviceStatus.dwControlsAccepted = 0;
        s_serviceStatus.dwCurrentState = SERVICE_STOPPED;
        s_serviceStatus.dwWin32ExitCode = 0;

        if (!SetServiceStatus(s_statusHandle, &s_serviceStatus)) {
          Log()->log_error("!SetServiceStatus SERVICE_STOPPED lastError {:#x}", GetLastError());
        }
      }
    },
    {0, 0}
  };

  if (!StartServiceCtrlDispatcher(serviceTable)) {
    if (ERROR_FAILED_SERVICE_CONTROLLER_CONNECT == GetLastError()) {
      // Run this exe as console.
      return;
    }

    Log()->log_error("!StartServiceCtrlDispatcher lastError {:#x}", GetLastError());

    ExitProcess(1);
  }

  Log()->log_info("Service exit");

  ExitProcess(0);
}