【问题标题】:Set system time using Qt through DBus通过 DBus 使用 Qt 设置系统时间
【发布时间】:2016-06-13 08:22:24
【问题描述】:

我尝试通过以下方式使用 Qt through DBus 设置系统时间:

#include <QDBusConnection>
#include <QDBusInterface>
#include <QDBusMessage>
#include <QDebug>
#include <QDateTime>
#include <cstdlib>

int main (int /*argc*/, char ** /*argv*/)
{
    QDBusConnection dbConnection = QDBusConnection::systemBus ();
    QDBusInterface dbInterface (
            "org.freedesktop.timedate1.set-time"
          , "/org/freedesktop/timedate1/set-time/Manager"
          , "org.freedesktop.timedate1.set-time.Manager"
          , dbConnection);
    qDebug () << "DBus interface validation: " << dbInterface.isValid ();
    if (dbInterface.isValid () ) {
        QDBusMessage dbMessage = dbInterface.call ("SetTime", QDateTime::currentDateTime ().toMSecsSinceEpoch () * 1000, false, false);
        qDebug () << "DBus message: " << dbMessage;
    }

    return EXIT_SUCCESS;
}

但我有:DBus interface validation: false

如果我在控制台中调用:

$ gdbus introspect \
      --system \
      --dest org.freedesktop.timedate1 \
      --object-path /org/freedesktop/timedate1

我得到了一些相关的输出(所以看起来环境没有问题):

node /org/freedesktop/timedate1 {
  interface org.freedesktop.DBus.Peer {
        ...
  };
  interface org.freedesktop.DBus.Introspectable {
        ...
  };
  interface org.freedesktop.DBus.Properties {
    methods:
        ...
    signals:
        ...
    properties:
  };
  interface org.freedesktop.timedate1 {
    methods:
      SetTime(in  x arg_0,
              in  b arg_1,
              in  b arg_2);
        ...
    signals:
    properties:
        ...
  };
};

源代码和构建脚本可在GitLab 获得。

【问题讨论】:

  • 看起来对象需要被实例化。当我查看d-feet 时 - 它不存在。然后我从终端启动$ gdbus introspect --system --dest org.freedesktop.timedate1 --object-path /org/freedesktop/timedate1 - timedate1 出现(然后 Qt 应用程序将其报告为有效)。
  • @Velkan, Looks like the object needs to be instantiated. 我不熟悉 D-Bus。你能解释一下我到底应该怎么做吗?

标签: qt dbus freedesktop.org


【解决方案1】:

有几个问题。

  1. 使用了错误的 D-Bus 命令。在尝试编写 Qt 程序之前,我必须使用控制台调试命令。所以正确的命令是:

    dbus-send \
        --system \
        --print-reply \
        --type=method_call \
        --dest='org.freedesktop.timedate1' \
               '/org/freedesktop/timedate1' \
                org.freedesktop.timedate1.SetTime \
                    int64:120000000 \
                    boolean:true \
                    boolean:false
    
  2. 使用 ntp 服务时,将执行命令并出现错误:Automatic time synchronization is enabled。所以(如建议here)必须禁用同步:

    timedatectl set-ntp 0
    
  3. 正如@Velcan 所说,定时服务处于非活动状态:

    服务在有人尝试访问该名称时启动 org.freedesktop.timedate1

    在我的环境 (KUbuntu 15.10 x86_64) 中,服务在上次调用后 30 秒处于活动状态。

  4. 根据Qt documentation

    bool QDBusAbstractInterface::isValid() const

    如果这是一个 对远程对象的有效引用。如果存在则返回 false 创建此接口期间的错误(例如,如果 远程应用程序不存在)。

    注意:在处理远程对象时,并不总是可以 在创建 QDBusInterface 时判断它是否存在。

  5. 即使QDBusAbstractInterface::isValid() 返回falsecall 函数也会成功执行。

  6. 最后,正确的代码非常简短:

    QDBusInterface dbInterface (
        "org.freedesktop.timedate1"
      , "/org/freedesktop/timedate1"
      , "org.freedesktop.timedate1"
      , QDBusConnection::systemBus () );
    qDebug () << dbInterface.call ("SetTime", 120000000ll, true, false);  
    

    此命令将时间设置为提前两分钟。

    感谢@Velkan 帮助解决问题并提供有用信息!

【讨论】:

    【解决方案2】:

    简而言之:QDBusInterface 创建的重试循环可以完成这项工作。

    我探索了更多。该 dbus 对象由systemd-timedated 服务提供。要了解其状态:

    sudo systemctl status systemd-timedated
    

    服务的配置在/lib/systemd/system/systemd-timedated.service:

    [Unit]
    Description=Time & Date Service
    Documentation=man:systemd-timedated.service(8) man:localtime(5)
    Documentation=http://www.freedesktop.org/wiki/Software/systemd/timedated
    
    [Service]
    ExecStart=/lib/systemd/systemd-timedated
    BusName=org.freedesktop.timedate1
    CapabilityBoundingSet=CAP_SYS_TIME
    WatchdogSec=1min
    PrivateTmp=yes
    ProtectSystem=yes
    ProtectHome=yes
    

    BusName 设置负责所谓的“服务的 D-Bus 激活”。因此,当有人尝试访问名称 org.freedesktop.timedate1 时,服务就会启动。

    但显然需要时间才能开始。我不知道它应该如何干净地完成,但你可以创建一个重试循环来创建QDBusInterface。你会看到sudo systemctl status systemd-timedated 被激活并且Qt 检索到一个有效的接口。

    我尝试过的对象名称和路径:

    QDBusInterface dbInterface (
        "org.freedesktop.timedate1"
        , "/org/freedesktop/timedate1"
        , "org.freedesktop.timedate1"
        , dbConnection);
    

    【讨论】:

    • 感谢您的回答,这对我非常有用!但在我看来,提出的解决方案还不够好。
    最近更新 更多