Home > OS >  How to call a D-Bus interface from Qt with struct argument
How to call a D-Bus interface from Qt with struct argument

Time:12-13

I'm trying to ask polkit for authorization but I can't seem to find out how to provide a struct as an argument.

QDBusArgument subject;
subject.beginStructure();
subject << "unix-process";
subject << QMap<QString, QVariant>{
    {"pid", static_cast<uint32_t>(QCoreApplication::applicationPid())},
    {"start-time", static_cast<uint64_t>(0)},
};
subject.endStructure();

QDBusInterface polkit("org.freedesktop.PolicyKit1", "/org/freedesktop/PolicyKit1/Authority", "org.freedesktop.PolicyKit1.Authority");
auto result = polkit.callWithArgumentList(
    QDBus::CallMode::AutoDetect,
    "CheckAuthorization",
    {
      // how to provide the subject here?
    }
);

The QDBusArgument shows what the argument should look like and I somehow need to translate this to a format that can be used in callWithArgumentList.

FreeDesktop specifies the struct as such:

{
  String               subject_kind,
  Dict<String,Variant> subject_details
}

More specifically, I'm trying to replace this with a dbus call:

// checkProcess is *QProcess
checkProcess->start("pkcheck", {
    "--process",
    QString::number(QCoreApplication::applicationPid()),
    "--action-id",
    "my-custom-action-id",
    "--allow-user-interaction"
});

CodePudding user response:

I'm not too familiar with D-Bus or its Qt interface, but I do happen to have a linux system I could test this on. It seems that you were already well on your way, I was able to get a response from the polkit service after some small adjustments:

#include <QtDBus>

int main() {
    // Not all Qt types are compatible with the D-Bus interface by default:
    // QMap<QString, QVariant> is, but QMap<QString, QString> is not.
    // If you need such a type, even if only to pass an empty
    // Dict<String, String>, it must first be registered like so:
    qDBusRegisterMetaType<QMap<QString, QString>>();

    // Subject kind should be provided as a QString, integers as Qt types
    QDBusArgument subject;
    subject.beginStructure();
    subject << QString("unix-process");
    subject << QMap<QString, QVariant>{
        {"pid", static_cast<quint32>(QCoreApplication::applicationPid())},
        {"start-time", static_cast<quint64>(0)},
    };
    subject.endStructure();

    // PolicyKit1 is on the system bus
    QDBusInterface polkit(
        "org.freedesktop.PolicyKit1",
        "/org/freedesktop/PolicyKit1/Authority",
        "org.freedesktop.PolicyKit1.Authority",
        QDBusConnection::systemBus()
    );

    // callWithArgumentList only takes actual QVariantLists as the arguments,
    // call is a more convenient variadic function template 
    auto result = polkit.call(
        QDBus::CallMode::AutoDetect,
        "CheckAuthorization",
        QVariant::fromValue(subject),
        "org.freedesktop.login1.set-user-linger",
        QVariant::fromValue(QMap<QString, QString>{}),
        0x1u, // AllowUserInteraction = 0x00000001
        ""
    );
    
    qInfo() << result;
}

This resulted in the following output:

QDBusMessage(type=MethodReturn, service=":1.1", signature="(bba{ss})", contents=([Argument: (bba{ss}) false, true, [Argument: a{ss} {"polkit.retains_authorization_after_challenge" = "1"}]]) )

Some further remarks:

  • Types registered with the D-Bus type system must be convertible to and from QDBusArgument by the << and >> operators, which is why you can run into issues when using those with non-Qt types. You can implement these operators for other types to allow registering them;
  • Types passed to a D-Bus call must be implicitly convertible to QVariant. If they are not, you can convert them explicitly using QVariant::fromValue().

Once these requirements are met, you should be able to get meaningful output from D-Bus, even if you pass the wrong arguments. For example, leaving out the last string argument to CheckAuthorization leads to an InvalidArgs error response with the message

Type of message, “((sa{sv})sa{ss}u)”, does not match expected type “((sa{sv})sa{ss}us)”

The string encodes the expected types

{
    { 
        String, 
        Dict<String, Variant>
    },
    String,
    Dict<String, String>,
    Uint32,
    String
}
  • Related