Search code examples
qtdbusfreedesktop.org

Set system time using Qt through DBus


I try to set system time using Qt through DBus in the following way:

#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;
}

But I've got: DBus interface validation: false.

If I call in the console:

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

I get some relevant output (so it looks like no problems with environment):

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:
        ...
  };
};

Source code and build script available at GitLab.


Solution

  • There are several problems.

    1. Wrong D-Bus command is used. Before try writing Qt program I must debug the command with console. So correct command is:

      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. When ntp service is used command will be performed with error: Automatic time synchronization is enabled. So (as suggested here) synchronization must be disabled:

      timedatectl set-ntp 0
      
    3. As mentioned by @Velcan timedated service is in inactive state:

      the service is started when someone tries to access the name org.freedesktop.timedate1

      In my environment (KUbuntu 15.10 x86_64) the service is in active state 30 seconds after last call.

    4. According to the Qt documentation:

      bool QDBusAbstractInterface::isValid() const

      Returns true if this is a valid reference to a remote object. It returns false if there was an error during the creation of this interface (for instance, if the remote application does not exist).

      Note: when dealing with remote objects, it is not always possible to determine if it exists when creating a QDBusInterface.

    5. Even if QDBusAbstractInterface::isValid() returns false the call function performs with successful result.

    6. So finally, correct code is very short and simple:

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

      This command sets time to two minutes ahead.

      Thanks @Velkan for help in solving the question and providing useful information!