Остановите службу systemd с помощью метода DBus - PullRequest
1 голос
/ 18 июня 2019

У меня есть серверное клиентское приложение, и я хочу правильно остановить сервер в Linux с помощью systemd.

Сервер и клиент реализованы на Java, работают на JRE 1.8.Я понял, что самый элегантный способ - запросить остановку сервера через DBus.Я реализовал связь DBus через модифицированную привязку DBus для Java (потому что реализация 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

Calling systemctl stop product_srv.service, похоже, отправляет SIGKILL процессу сервера после успешного выполнения dbus-send и не ждет, когда сервер будет корректно завершен.Я попытался установить TimeoutStopSec на 15 секунд, но он не работает.

Как правильно остановить службу systemd через DBus?Нужно ли реализовывать клиент 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();
         }
      }
   }
}
...