aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/org/traccar/WindowsService.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/org/traccar/WindowsService.java')
-rw-r--r--src/main/java/org/traccar/WindowsService.java231
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;
+ }
+
+ }
+
+}