diff options
Diffstat (limited to 'src/main/java/org/traccar/WindowsService.java')
-rw-r--r-- | src/main/java/org/traccar/WindowsService.java | 231 |
1 files changed, 231 insertions, 0 deletions
diff --git a/src/main/java/org/traccar/WindowsService.java b/src/main/java/org/traccar/WindowsService.java new file mode 100644 index 000000000..4a8955608 --- /dev/null +++ b/src/main/java/org/traccar/WindowsService.java @@ -0,0 +1,231 @@ +/* + * Copyright 2018 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar; + +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Advapi32; +import com.sun.jna.platform.win32.WinError; +import com.sun.jna.platform.win32.WinNT; +import com.sun.jna.platform.win32.Winsvc; +import com.sun.jna.platform.win32.Winsvc.HandlerEx; +import com.sun.jna.platform.win32.Winsvc.SC_HANDLE; +import com.sun.jna.platform.win32.Winsvc.SERVICE_DESCRIPTION; +import com.sun.jna.platform.win32.Winsvc.SERVICE_MAIN_FUNCTION; +import com.sun.jna.platform.win32.Winsvc.SERVICE_STATUS; +import com.sun.jna.platform.win32.Winsvc.SERVICE_STATUS_HANDLE; +import com.sun.jna.platform.win32.Winsvc.SERVICE_TABLE_ENTRY; +import jnr.posix.POSIXFactory; + +import java.io.File; +import java.net.URISyntaxException; + +public abstract class WindowsService { + + private static final Advapi32 ADVAPI_32 = Advapi32.INSTANCE; + + private final Object waitObject = new Object(); + + private String serviceName; + private ServiceMain serviceMain; + private ServiceControl serviceControl; + private SERVICE_STATUS_HANDLE serviceStatusHandle; + + public WindowsService(String serviceName) { + this.serviceName = serviceName; + } + + public boolean install( + String displayName, String description, String[] dependencies, + String account, String password, String config) throws URISyntaxException { + + String javaHome = System.getProperty("java.home"); + String javaBinary = javaHome + "\\bin\\java.exe"; + + File jar = new File(WindowsService.class.getProtectionDomain().getCodeSource().getLocation().toURI()); + String command = javaBinary + + " -Duser.dir=\"" + jar.getParentFile().getAbsolutePath() + "\"" + + " -jar \"" + jar.getAbsolutePath() + "\"" + + " --service \"" + config + "\""; + + boolean success = false; + StringBuilder dep = new StringBuilder(); + + if (dependencies != null) { + for (String s : dependencies) { + dep.append(s); + dep.append("\0"); + } + } + dep.append("\0"); + + SERVICE_DESCRIPTION desc = new SERVICE_DESCRIPTION(); + desc.lpDescription = description; + + SC_HANDLE serviceManager = openServiceControlManager(null, Winsvc.SC_MANAGER_ALL_ACCESS); + + if (serviceManager != null) { + SC_HANDLE service = ADVAPI_32.CreateService(serviceManager, serviceName, displayName, + Winsvc.SERVICE_ALL_ACCESS, WinNT.SERVICE_WIN32_OWN_PROCESS, WinNT.SERVICE_AUTO_START, + WinNT.SERVICE_ERROR_NORMAL, + command, + null, null, dep.toString(), account, password); + + if (service != null) { + success = ADVAPI_32.ChangeServiceConfig2(service, Winsvc.SERVICE_CONFIG_DESCRIPTION, desc); + ADVAPI_32.CloseServiceHandle(service); + } + ADVAPI_32.CloseServiceHandle(serviceManager); + } + return success; + } + + public boolean uninstall() { + boolean success = false; + + SC_HANDLE serviceManager = openServiceControlManager(null, Winsvc.SC_MANAGER_ALL_ACCESS); + + if (serviceManager != null) { + SC_HANDLE service = ADVAPI_32.OpenService(serviceManager, serviceName, Winsvc.SERVICE_ALL_ACCESS); + + if (service != null) { + success = ADVAPI_32.DeleteService(service); + ADVAPI_32.CloseServiceHandle(service); + } + ADVAPI_32.CloseServiceHandle(serviceManager); + } + return success; + } + + public boolean start() { + boolean success = false; + + SC_HANDLE serviceManager = openServiceControlManager(null, WinNT.GENERIC_EXECUTE); + + if (serviceManager != null) { + SC_HANDLE service = ADVAPI_32.OpenService(serviceManager, serviceName, WinNT.GENERIC_EXECUTE); + + if (service != null) { + success = ADVAPI_32.StartService(service, 0, null); + ADVAPI_32.CloseServiceHandle(service); + } + ADVAPI_32.CloseServiceHandle(serviceManager); + } + + return success; + } + + public boolean stop() { + boolean success = false; + + SC_HANDLE serviceManager = openServiceControlManager(null, WinNT.GENERIC_EXECUTE); + + if (serviceManager != null) { + SC_HANDLE service = Advapi32.INSTANCE.OpenService(serviceManager, serviceName, WinNT.GENERIC_EXECUTE); + + if (service != null) { + SERVICE_STATUS serviceStatus = new SERVICE_STATUS(); + success = Advapi32.INSTANCE.ControlService(service, Winsvc.SERVICE_CONTROL_STOP, serviceStatus); + Advapi32.INSTANCE.CloseServiceHandle(service); + } + Advapi32.INSTANCE.CloseServiceHandle(serviceManager); + } + + return success; + } + + public void init() throws URISyntaxException { + String path = new File( + WindowsService.class.getProtectionDomain().getCodeSource().getLocation().toURI()).getParent(); + + POSIXFactory.getPOSIX().chdir(path); + + serviceMain = new ServiceMain(); + SERVICE_TABLE_ENTRY entry = new SERVICE_TABLE_ENTRY(); + entry.lpServiceName = serviceName; + entry.lpServiceProc = serviceMain; + + Advapi32.INSTANCE.StartServiceCtrlDispatcher((SERVICE_TABLE_ENTRY[]) entry.toArray(2)); + } + + private SC_HANDLE openServiceControlManager(String machine, int access) { + return ADVAPI_32.OpenSCManager(machine, null, access); + } + + private void reportStatus(int status, int win32ExitCode, int waitHint) { + SERVICE_STATUS serviceStatus = new SERVICE_STATUS(); + serviceStatus.dwServiceType = WinNT.SERVICE_WIN32_OWN_PROCESS; + serviceStatus.dwControlsAccepted = Winsvc.SERVICE_ACCEPT_STOP | Winsvc.SERVICE_ACCEPT_SHUTDOWN; + serviceStatus.dwWin32ExitCode = win32ExitCode; + serviceStatus.dwWaitHint = waitHint; + serviceStatus.dwCurrentState = status; + + ADVAPI_32.SetServiceStatus(serviceStatusHandle, serviceStatus); + } + + public abstract void run(); + + private class ServiceMain implements SERVICE_MAIN_FUNCTION { + + public void callback(int dwArgc, Pointer lpszArgv) { + serviceControl = new ServiceControl(); + serviceStatusHandle = ADVAPI_32.RegisterServiceCtrlHandlerEx(serviceName, serviceControl, null); + + reportStatus(Winsvc.SERVICE_START_PENDING, WinError.NO_ERROR, 3000); + reportStatus(Winsvc.SERVICE_RUNNING, WinError.NO_ERROR, 0); + + Thread.currentThread().setContextClassLoader(WindowsService.class.getClassLoader()); + + run(); + + try { + synchronized (waitObject) { + waitObject.wait(); + } + } catch (InterruptedException ex) { + } + + reportStatus(Winsvc.SERVICE_STOPPED, WinError.NO_ERROR, 0); + + // Avoid returning from ServiceMain, which will cause a crash + // See http://support.microsoft.com/kb/201349, which recommends + // having init() wait for this thread. + // Waiting on this thread in init() won't fix the crash, though. + + System.exit(0); + } + + } + + private class ServiceControl implements HandlerEx { + + public int callback(int dwControl, int dwEventType, Pointer lpEventData, Pointer lpContext) { + switch (dwControl) { + case Winsvc.SERVICE_CONTROL_STOP: + case Winsvc.SERVICE_CONTROL_SHUTDOWN: + reportStatus(Winsvc.SERVICE_STOP_PENDING, WinError.NO_ERROR, 5000); + synchronized (waitObject) { + waitObject.notifyAll(); + } + break; + default: + break; + } + return WinError.NO_ERROR; + } + + } + +} |