0

我尝试通过 DBus使用 Qt 设置系统时间,方法如下:

#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提供源代码和构建脚本。

4

2 回答 2

4

有几个问题。

  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。因此(如此处所建议必须禁用同步:

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

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

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

  4. 根据Qt 文档

    bool QDBusAbstractInterface::isValid() 常量

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

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

  5. 即使QDBusAbstractInterface::isValid()返回函数执行成功false的结果。call

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

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

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

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

于 2016-06-14T18:03:16.090 回答
1

简而言之: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 激活”。因此,当有人尝试访问 name 时,服务就会启动org.freedesktop.timedate1

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

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

QDBusInterface dbInterface (
    "org.freedesktop.timedate1"
    , "/org/freedesktop/timedate1"
    , "org.freedesktop.timedate1"
    , dbConnection);
于 2016-06-14T06:44:15.637 回答