Russian Qt Forum

Qt => Общие вопросы => Тема начата: Vladimir от Май 07, 2013, 10:39



Название: [РЕШЕНО] Выключение компьютера через D-Bus
Отправлено: Vladimir от Май 07, 2013, 10:39
Доброго дня! :) как-то давно спрашивал, как можно выключить программно компьютер под управлением Linux не из-под root пользователя! Посоветовали использовать D-Bus. Пробую, но не получается.. причем залочить экран получается, а вот выключить компьютер нет. Вот код:

Код:
#include <QtCore/QCoreApplication>
#include <QtCore/QTimer>
#include <QtDBus/QtDBus>

#define SERVICE_NAME  "org.freedesktop.ConsoleKit.Manager"
//#define SERVICE_NAME "org.freedesktop.ScreenSaver"

int main(int argc, char **argv)
{
     QCoreApplication app(argc, argv);

     // Создаем соединение с системной шиной D-Bus
     QDBusConnection conn = QDBusConnection::systemBus();

     if (!conn.isConnected()) {
         qDebug() << "Err: isConnected systemBus";
         return 1;
     }

     if (!conn.registerService(SERVICE_NAME)) {
         qDebug() << "Err: registerService";
         exit(1);
     }

     QDBusMessage msg = QDBusMessage::createMethodCall("org.freedesktop.ConsoleKit.Manager",
                                                       "/Manager",
                                                       "org.freedesktop.ConsoleKit.Manager",
                                                       "Stop");

//   QDBusMessage msg = QDBusMessage::createMethodCall("org.freedesktop.ScreenSaver",
//                                                     "/ScreenSaver",
//                                                     "org.freedesktop.ScreenSaver",
//                                                     "Lock");

     conn.systemBus().send(msg);
     return 0;
}

Для регисрации сервиса добавил в /etc/dbus-1/system.d файл myProg.conf
Код:
<!DOCTYPE busconfig PUBLIC
 "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
 "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
  <policy context="default">
    <allow own="org.freedesktop.ConsoleKit.Manager"/>
    <allow send_destination="org.freedesktop.ConsoleKit.Manager"/>
    <allow receive_sender  ="org.freedesktop.ConsoleKit.Manager"/>
 </policy>
</busconfig>

Предлагалось использовать HAL, но в используемом дистрибутиве OpenSUSE 11.4 он был урезан. Даже из терминала не выключает
Код:
dbus-send --system --print-reply --dest="org.freedesktop.Hal" /org/freedesktop/Hal/devices/computer org.freedesktop.Hal.Device.SystemPowerManagement.Shutdown

А вот через ConsoleKit выключает, но только через терминал
Код:
dbus-send --system --print-reply --dest="org.freedesktop.ConsoleKit" /org/freedesktop/ConsoleKit/Manager org.freedesktop.ConsoleKit.Manager.Stop

Что я в программе делаю не так???




Название: Re: Выключение компьютера через D-Bus
Отправлено: Пантер от Май 07, 2013, 10:46
А у юзера есть соответствующие права?


Название: Re: Выключение компьютера через D-Bus
Отправлено: Vladimir от Май 07, 2013, 10:51
А у юзера есть соответствующие права?

Хороший вопрос)) а как это посмотреть/назначить в Linux? Он должен входить в группу root? У меня локальный пользователь входит во все группы) ну так, чтобы уже наверняка.)


Название: Re: Выключение компьютера через D-Bus
Отправлено: Пантер от Май 07, 2013, 11:13
Почитать маны или гугл. :)
На сколько я помню, группа wheel.


Название: Re: Выключение компьютера через D-Bus
Отправлено: Vladimir от Май 07, 2013, 11:31
Почитать маны или гугл. :)
На сколько я помню, группа wheel.

Да, есть такая группа и он в нее входит. Я же написал, что пользователь входит во все группы. Может дело в коде или файле myProg.conf?
Потому что в вызове
Код:
QDBusMessage msg = QDBusMessage::createMethodCall("org.freedesktop.ScreenSaver",
                                                     [b]"/ScreenSaver",[/b]
                                                     "org.freedesktop.ScreenSaver",
                                                     "Lock");
Если указать /org/freedesktop/ScreenSaver вместо /ScreenSaver, то экран не лочится.. хотя вроде как тоже самое, только полный путь..
Может нужно использовать QDBusMessage::createSignal() ? Пробовал задавать разные входные параметры, но не с одним не работает..

еще есть org.kde.KSMServerInterface. но с ним тоже не получается, видимо где-то неправильно задаю параметры вызова..? как нужно?


Название: Re: Выключение компьютера через D-Bus
Отправлено: Пантер от Май 07, 2013, 11:34
Попробуй пошуршать на qt-apps, по-любому кто-то уже подобное делал. Я с дбасом не работал, так что, помочь не могу.


Название: Re: Выключение компьютера через D-Bus
Отправлено: Vladimir от Май 07, 2013, 11:41
Попробуй пошуршать на qt-apps, по-любому кто-то уже подобное делал. Я с дбасом не работал, так что, помочь не могу.

Спасибо за совет. может кто работал, подскажет еще что)


Название: Re: Выключение компьютера через D-Bus
Отправлено: Old от Май 07, 2013, 14:25
Попробуй так:
Код
C++ (Qt)
QDBusMessage msg = QDBusMessage::createMethodCall("org.freedesktop.ConsoleKit",
                                                      "/org/freedesktop/ConsoleKit/Manager",
                                                      "org.freedesktop.ConsoleKit.Manager",
                                                      "Stop");
 


Название: Re: Выключение компьютера через D-Bus
Отправлено: Vladimir от Май 07, 2013, 14:39
Попробуй так:
Код
C++ (Qt)
QDBusMessage msg = QDBusMessage::createMethodCall("org.freedesktop.ConsoleKit",
                                                      "/org/freedesktop/ConsoleKit/Manager",
                                                      "org.freedesktop.ConsoleKit.Manager",
                                                      "Stop");
 

и так пробовал. тоже не работает :(


Название: Re: Выключение компьютера через D-Bus
Отправлено: Old от Май 07, 2013, 14:47
и так пробовал. тоже не работает :(
Запусти qdbusviewer и попробуй через него отключить.


Название: Re: Выключение компьютера через D-Bus
Отправлено: Vladimir от Май 07, 2013, 14:56
и так пробовал. тоже не работает :(
Запусти qdbusviewer и попробуй через него отключить.


ух ты. это типо такая прога визуализации команд D-Bus? круто) да, через него отключил теми же командами
org->freedesktop->ConsoleKit->Manager->Stop. так и через терминал ConsoleKit отключает, почему тогда программно не хочет??


Название: Re: Выключение компьютера через D-Bus
Отправлено: Old от Май 07, 2013, 15:14
.почему тогда программно не хочет??
Что-то делаешь не так. Посмотри ее исходники, как она вызывает методы.


Название: Re: Выключение компьютера через D-Bus
Отправлено: Vladimir от Май 07, 2013, 15:23
.почему тогда программно не хочет??
Что-то делаешь не так. Посмотри ее исходники, как она вызывает методы.


да, я и подумал глянуть исходники, а где их можно взять?! что-то в инете не получается найти..


Название: Re: Выключение компьютера через D-Bus
Отправлено: Alex Custov от Май 07, 2013, 15:24
ты что-то не так понял, registerService() регистрирует свой сервис, а тебе нужно подключиться к существующему. И конфигурацию в system.d для этого не нужно класть, это нужно только для сервиса dbus, а у тебя клиент.


Название: Re: Выключение компьютера через D-Bus
Отправлено: Alex Custov от Май 07, 2013, 15:34
вот так попробуй

Код
C++ (Qt)
   QDBusInterface remoteApp("org.freedesktop.ConsoleKit",
                               "/org/freedesktop/ConsoleKit/Manager",
                               "org.freedesktop.ConsoleKit.Manager",
                               QDBusConnection::systemBus());
 
   remoteApp.call("Stop"); // нужно ещё проверить ответ если он есть и вывести ошибку если есть
 


Название: Re: Выключение компьютера через D-Bus
Отправлено: Vladimir от Май 07, 2013, 15:43
вот так попробуй

Код
C++ (Qt)
   QDBusInterface remoteApp("org.freedesktop.ConsoleKit",
                               "/org/freedesktop/ConsoleKit/Manager",
                               "org.freedesktop.ConsoleKit.Manager",
                               QDBusConnection::systemBus());
 
   remoteApp.call("Stop"); // нужно ещё проверить ответ если он есть и вывести ошибку если есть
 

Сделал так, проверил ответ и ошибку! Ответа нет, ошибки нет..
не проходит проверку if(remoteApp.isValid())

Код:
int main(int argc, char **argv)
{
     QCoreApplication app(argc, argv);

     // Создаем соединение с системной шиной D-Bus
     QDBusConnection conn = QDBusConnection::systemBus();

     if (!conn.isConnected()) {
         qDebug() << "Err: isConnected systemBus";
         return 1;
     }

     QDBusInterface iface("org.freedesktop.ConsoleKit.Manager",
                          "/org/freedesktop/ConsoleKit/Manager",
                          "org.freedesktop.ConsoleKit.Manager",
                          QDBusConnection::systemBus());

     if (iface.isValid())
     {
         qDebug() << "iface.isValid()";
         QDBusReply<QString> reply = iface.call("Stop");
         if (reply.isValid()) {
             printf("Reply was: %s\n", qPrintable(reply.value()));
             //return 0;
         }

         fprintf(stderr, "Call failed: %s\n", qPrintable(reply.error().message()));
         //return 1;
     }
     else
     {
        qDebug() << "iface not valid";
     }
     return 0;
}

iface not valid


Название: Re: Выключение компьютера через D-Bus
Отправлено: tester64 от Май 07, 2013, 22:09
Можно использовать D-Bus интерфейсы ConsoleKit, UPower, HAL, и десктоп-специфичные интерфейсы.
Какие из D-Bus интерфейсов будут работать - сказать трудно.
У меня на OpenSUSE 12.3 работает только UPower, но возможно, что он не позволяет выполнять выключение и перезагрузку (во всяком случае, в коде KShutdown такого нет, а спецификацию искать было лень).

Вот навскидку работающий вариант для KDE:
Код
C++ (Qt)
#include <kworkspace/kworkspace.h>
[...]
QDBusInterface dbiConsoleKit("org.freedesktop.ConsoleKit", "/org/freedesktop/ConsoleKit/Manager", "org.freedesktop.ConsoleKit.Manager", QDBusConnection::systemBus());
QDBusInterface dbiUPower("org.freedesktop.UPower", "/org/freedesktop/UPower", "org.freedesktop.UPower", QDBusConnection::systemBus());
QDBusInterface dbiHal("org.freedesktop.Hal", "/org/freedesktop/Hal/devices/computer", "org.freedesktop.Hal.Device.SystemPowerManagement", QDBusConnection::systemBus());
QList<QDBusInterface*> dbis({&dbiConsoleKit, &dbiUPower, &dbiHal, nullptr});
QDBusMessage m;
QDBusError e;
QList<QDBusError::ErrorType> allowed({QDBusError::NoError, QDBusError::NoReply, QDBusError::Other});
foreach (QDBusInterface *dbi, dbis) {
if (dbi != nullptr && !dbi->isValid()) continue;
if (query == "system:halt") {
if (dbi == &dbiConsoleKit) m = dbi->call("Stop");
else if (dbi == &dbiHal) m = dbi->call("Shutdown");
else if (dbi == nullptr) KWorkSpace::requestShutDown(KWorkSpace::ShutdownConfirmNo, KWorkSpace::ShutdownTypeHalt, KWorkSpace::ShutdownModeForceNow); // именно "ShutdownTypeHalt", а не то, что написано в "kworkspace.h": "Temporary brain damage. Don't use. Same as ShutdownTypeNone"
else continue;
} else if (query == "system:reboot") {
if (dbi == &dbiConsoleKit) m = dbi->call("Restart");
else if (dbi == &dbiHal) m = dbi->call("Reboot");
else if (dbi == nullptr) KWorkSpace::requestShutDown(KWorkSpace::ShutdownConfirmNo, KWorkSpace::ShutdownTypeReboot, KWorkSpace::ShutdownModeForceNow);
else continue;
} else if [...]
}
e = dbi->lastError();
if (allowed.contains(e.type())) { ok = true; break; }
}

Если нужен другой десктоп или список по-максимуму, то наверное будет проще содрать из KShutdown.


Название: Re: Выключение компьютера через D-Bus
Отправлено: Alex Custov от Май 08, 2013, 00:06
Сделал так, проверил ответ и ошибку! Ответа нет, ошибки нет..
не проходит проверку if(remoteApp.isValid())

Думаю дело в правах или неправильном использовании интерфейса consolekit (в котором я не специалист).


Название: Re: Выключение компьютера через D-Bus
Отправлено: Vladimir от Май 08, 2013, 10:34
Сделал так, проверил ответ и ошибку! Ответа нет, ошибки нет..
не проходит проверку if(remoteApp.isValid())

Думаю дело в правах или неправильном использовании интерфейса consolekit (в котором я не специалист).

Да, дело было в неправильном использовании/написании интерфейса! Правильное написание

QDBusInterface dbiConsoleKit("org.freedesktop.ConsoleKit", "/org/freedesktop/ConsoleKit/Manager", "org.freedesktop.ConsoleKit.Manager", QDBusConnection::systemBus());


вместо

QDBusInterface iface("org.freedesktop.ConsoleKit.Manager",
                          "/org/freedesktop/ConsoleKit/Manager",
                          "org.freedesktop.ConsoleKit.Manager",
                          QDBusConnection::systemBus());


Спасибо ВСЕМ за помощь! =)


Название: Re: Выключение компьютера через D-Bus
Отправлено: Alex Custov от Май 08, 2013, 13:16
Да, дело было в неправильном использовании/написании интерфейса! Правильное написание

Ну да, я не обратил внимание на то, что ты имя сервиса написал неправильно, хотя в моём сообщении было правильно.