使用Qt到DBus设置系统时间

时间:2016-06-13 08:22:25

标签: qt dbus freedesktop.org

我尝试使用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提供的源代码和构建脚本。

2 个答案:

答案 0 :(得分: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激活&#39;。因此,当有人试图访问名称org.freedesktop.timedate1时,服务就会启动。

但显然需要时间才能开始。我不知道它应该如何干净利落地完成,但你可以创建一个创建QDBusInterface的重试循环。您将看到sudo systemctl status systemd-timedated变为活动状态,Qt将检索有效的接口。

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

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

答案 1 :(得分: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

         

    如果是a,则返回true   对远程对象的有效引用。如果有,则返回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帮助解决问题并提供有用的信息!