使用DBus方法停止systemd服务

时间:2019-06-18 08:25:58

标签: java server systemd dbus

我有一个服务器客户端应用程序,我想使用systemd在Linux上正确停止服务器。

服务器和客户端以Java实现,在JRE 1.8上运行。我发现最优雅的方法是通过DBus请求服务器停止。我在modified DBus binding for Java上实现了DBus通信(因为freedesktop.org的实现似乎不受限制并且被破坏了)。

服务器平均大约需要1-2秒才能正常关闭。

我通过调用成功测试了服务器关闭是否可以通过控制台正常工作

$> /usr/bin/dbus-send --system --print-reply --dest=com.xxx.server.Server /com/xxx/server/Server com.xxx.server.Server.DBusInterfaceImpl.stopServer

systemctl stop product_srv.service成功之后,调用SIGKILL似乎向服务器进程发送了dbus-send,并且没有等到服务器正确关闭后才进行。我试图将TimeoutStopSec设置为15秒,但这不起作用。

如何通过DBus正确停止systemd服务? 我是否必须实现DBus客户端来停止服务器,或者dbus-send是否可以对服务文件进行一些更改?

示例服务器:

implementation 'com.github.hypfvieh:dbus-java:2.7.1'

服务文件/etc/systemd/system/product_srv.service

[Unit]
Description=Product Server
After=network.target mysql.service
Requires=mysql.service

[Service]
Environment=PRODUCT_SRV_PID=/var/run/product_srv/product_srv.pid
Environment=JAVA_HOME=/opt/xxx/java-jre18-x64
Environment=PRODUCT_HOME=/opt/xxx/product_srv
Environment=LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/lib/jni

# assembles the classpath of the server
ExecStart=/opt/xxx/product_srv/bin/server_systemd.sh start
TimeoutStopSec=15s
ExecStop=/usr/bin/dbus-send --system --print-reply --dest=com.xxx.server.Server /com/xxx/server/Server com.xxx.server.Server.DBusInterfaceImpl.stopServer

Type=dbus
BusName=com.xxx.server.Server

[Install]
WantedBy=multi-user.target

策略配置/etc/dbus-1/system.d/product_srv.conf

<!DOCTYPE busconfig PUBLIC
          "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
          "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
    <policy user="root">
        <allow own="com.xxx.server.Server"/>
        <allow send_destination="com.xxx.server.Server"/>
        <allow receive_sender="com.xxx.server.Server"/>
    </policy>
</busconfig>

Server.java

package com.xxx.server;

import org.freedesktop.dbus.DBusConnection;
import org.freedesktop.dbus.DBusInterface;
import org.freedesktop.dbus.exceptions.DBusException;

import com.xxx.server.dbus.DBusClient;

public class Server{

   public class DBusInterfaceImpl implements DBusInterface{

      public void stopServer(){

         Server.this.stopServer();
      }

      @Override
      public boolean isRemote(){

         return false;
      }
   }

   public volatile boolean running = true;
   private DBusClient      dBusClient;

   public Server(){

      System.out.println("Starting the server");
      // start some fancy server sockets (run in different threads)

      try{
         this.dBusClient = new DBusClient("com.xxx.server.Server",
                                          DBusConnection.SYSTEM);
         this.dBusClient.exportObject("/com/xxx/server/Server",
                                      new DBusInterfaceImpl());
      }
      catch(DBusException e){
        e.printStackTrace();
      }
      System.out.println("Started the server");

      // let the main thread wait until stopping
      synchronized(this){
         while(this.running){
            try{
               this.wait();
            }
            catch(InterruptedException e){
               e.printStackTrace();
            }
         }
      }

      // stop fancy server sockets

      System.out.println("Stopped the server");

      if (this.dBusClient != null){
         try{
            this.dBusClient.unExportObject("/com/xxx/server/Server");
            this.dBusClient.stop();
         }
         catch(DBusException e){
            e.printStackTrace();
         }
      }
   }

   public static void main(String[] args){

      new Server();
   }

   public void stopServer(){

      System.out.println("Stopping the server ...");

      // don't stop the server immediately to prevent dbus-send
      // failing before it receives a reply that the invocation
      // is successful
      new Thread(() -> {

         try{
            Thread.sleep(20);
         }
         catch(InterruptedException e){
            e.printStackTrace();
         }

         synchronized(this){
            this.running = false;
            this.notifyAll();
         }
      },
                 "StopServerThread").start();
   }
}

DBusClient.java

package com.xxx.server.dbus;

import org.freedesktop.dbus.DBusConnection;
import org.freedesktop.dbus.DBusInterface;
import org.freedesktop.dbus.exceptions.DBusException;

public class DBusClient{

   private DBusConnection bus;
   private String         interfaceName;

   public DBusClient(String interfaceName,
                     int dBusSession) throws DBusException{

      this.bus = DBusConnection.getConnection(dBusSession);
      this.interfaceName = interfaceName;
      this.requestInterfaceName();
   }

   private void requestInterfaceName() throws DBusException{

      if (this.bus != null && this.interfaceName != null && !this.interfaceName.isEmpty()){
         synchronized(this.bus){
            this.bus.requestBusName(this.interfaceName);
         }
      }
   }

   public void exportObject(String busName,
                            DBusInterface dBusInterfaceImpl) throws DBusException{

      if (this.bus != null && busName != null && !busName.isEmpty() && dBusInterfaceImpl != null){
         synchronized(this.bus){
            this.bus.exportObject(busName,
                                  dBusInterfaceImpl);
         }
      }
   }

   public void unExportObject(String busName){

      if (this.bus != null && busName != null && !busName.isEmpty()){
         synchronized(this.bus){
            this.bus.unExportObject(busName);
         }
      }
   }

   public void stop() throws DBusException{

      if (this.bus != null){
         synchronized(this.bus){
            if (this.interfaceName != null && !this.interfaceName.isEmpty()){
               this.bus.releaseBusName(this.interfaceName);
            }
            this.bus.disconnect();
         }
      }
   }
}

0 个答案:

没有答案