systemdcommunicator.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. /*
  2. * Copyright (C) 2015 Malte Veerman <malte.veerman@gmail.com>
  3. *
  4. * This program is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU Lesser General Public License as published by
  6. * the Free Software Foundation; either version 2 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU Lesser General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU Lesser General Public License along
  15. * with this program; if not, write to the Free Software Foundation, Inc.,
  16. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  17. *
  18. */
  19. #include "systemdcommunicator.h"
  20. #include "fancontrolaction.h"
  21. #include "guibase.h"
  22. #include <QtCore/QTimer>
  23. #include <QtDBus/QDBusArgument>
  24. #include <QtDBus/QDBusInterface>
  25. #include <QtDBus/QDBusMetaType>
  26. #include <QtDBus/QDBusReply>
  27. #include <QtDBus/QDBusVariant>
  28. #include <KAuth/KAuthExecuteJob>
  29. #include <KI18n/KLocalizedString>
  30. #ifndef STANDARD_SERVICE_NAME
  31. #define STANDARD_SERVICE_NAME "fancontrol"
  32. #endif
  33. typedef struct
  34. {
  35. QString path;
  36. QString state;
  37. } SystemdUnitFile;
  38. Q_DECLARE_METATYPE(SystemdUnitFile)
  39. typedef QList<SystemdUnitFile> SystemdUnitFileList;
  40. Q_DECLARE_METATYPE(SystemdUnitFileList)
  41. QDBusArgument& operator <<(QDBusArgument &argument, const SystemdUnitFile &unitFile)
  42. {
  43. argument.beginStructure();
  44. argument << unitFile.path << unitFile.state;
  45. argument.endStructure();
  46. return argument;
  47. }
  48. const QDBusArgument& operator >>(const QDBusArgument &argument, SystemdUnitFile &unitFile)
  49. {
  50. argument.beginStructure();
  51. argument >> unitFile.path >> unitFile.state;
  52. argument.endStructure();
  53. return argument;
  54. }
  55. namespace Fancontrol
  56. {
  57. SystemdCommunicator::SystemdCommunicator(GUIBase *parent, const QString &serviceName) : QObject(parent),
  58. m_managerInterface(new QDBusInterface(QStringLiteral("org.freedesktop.systemd1"),
  59. QStringLiteral("/org/freedesktop/systemd1"),
  60. QStringLiteral("org.freedesktop.systemd1.Manager"),
  61. QDBusConnection::systemBus(),
  62. this)),
  63. m_serviceInterface(Q_NULLPTR)
  64. {
  65. if (!m_managerInterface || !m_managerInterface->isValid())
  66. emit error(i18n("Unable to init systemd dbus manager interface!"), true);
  67. if (parent)
  68. {
  69. connect(this, &SystemdCommunicator::error, parent, &GUIBase::handleError);
  70. connect(this, &SystemdCommunicator::info, parent, &GUIBase::handleInfo);
  71. }
  72. if (serviceName.isEmpty())
  73. setServiceName(QStringLiteral(STANDARD_SERVICE_NAME));
  74. else
  75. setServiceName(serviceName);
  76. m_serviceActive = systemdServiceActive();
  77. m_serviceEnabled = systemdServiceEnabled();
  78. }
  79. void SystemdCommunicator::setServiceName(const QString &name)
  80. {
  81. if (name != m_serviceName)
  82. {
  83. if (m_serviceInterface)
  84. {
  85. QDBusConnection::systemBus().disconnect(QStringLiteral("org.freedesktop.systemd1"),
  86. m_serviceObjectPath,
  87. QStringLiteral("org.freedesktop.DBus.Properties"),
  88. QStringLiteral("PropertiesChanged"),
  89. this,
  90. SLOT(updateServiceProperties(QString, QVariantMap, QStringList)));
  91. m_serviceInterface->deleteLater();
  92. m_serviceInterface = Q_NULLPTR;
  93. }
  94. m_serviceName = name;
  95. emit serviceNameChanged();
  96. emit info(i18n("New service name: \'%1\'", m_serviceName));
  97. if (serviceExists())
  98. {
  99. QVariantList arguments;
  100. arguments << QVariant(m_serviceName + ".service");
  101. const auto dbusreply = m_managerInterface->callWithArgumentList(QDBus::AutoDetect, QStringLiteral("LoadUnit"), arguments);
  102. if (dbusreply.type() == QDBusMessage::ErrorMessage)
  103. {
  104. emit error(dbusreply.errorMessage());
  105. m_serviceObjectPath.clear();
  106. }
  107. else if (dbusreply.type() == QDBusMessage::ReplyMessage)
  108. {
  109. m_serviceObjectPath = qdbus_cast<QDBusObjectPath>(dbusreply.arguments().at(0)).path();
  110. m_serviceInterface = new QDBusInterface(QStringLiteral("org.freedesktop.systemd1"),
  111. m_serviceObjectPath,
  112. QStringLiteral("org.freedesktop.systemd1.Unit"),
  113. QDBusConnection::systemBus(),
  114. this);
  115. if (!m_serviceInterface || !m_serviceInterface->isValid())
  116. emit error(i18n("Unable to init systemd dbus service interface: %1", m_serviceInterface->lastError().message()), true);
  117. QDBusConnection::systemBus().connect(QStringLiteral("org.freedesktop.systemd1"),
  118. m_serviceObjectPath,
  119. QStringLiteral("org.freedesktop.DBus.Properties"),
  120. QStringLiteral("PropertiesChanged"),
  121. this,
  122. SLOT(updateServiceProperties(QString, QVariantMap, QStringList)));
  123. }
  124. else
  125. emit error(i18n("Dbus reply message is not of type \'QDBusMessage::ReplyMessage\'"));
  126. }
  127. emit serviceEnabledChanged();
  128. emit serviceActiveChanged();
  129. emit needsApplyChanged();
  130. }
  131. }
  132. bool SystemdCommunicator::serviceExists() const
  133. {
  134. if (m_serviceInterface && m_serviceInterface->isValid())
  135. return true;
  136. QDBusMessage dbusreply;
  137. if (m_managerInterface && m_managerInterface->isValid())
  138. dbusreply = m_managerInterface->call(QDBus::AutoDetect, QStringLiteral("ListUnitFiles"));
  139. else
  140. {
  141. emit error(i18n("Systemd dbus manager interface not initialized!"), true);
  142. return false;
  143. }
  144. if (dbusreply.type() == QDBusMessage::ErrorMessage)
  145. {
  146. emit error(dbusreply.errorMessage());
  147. return false;
  148. }
  149. else if (dbusreply.type() == QDBusMessage::InvalidMessage)
  150. {
  151. emit error(i18n("Dbus returned invalid answer"));
  152. return false;
  153. }
  154. else if (dbusreply.signature() != QStringLiteral("a(ss)"))
  155. {
  156. emit error(i18n("Dbus returned answer with wrong signature: \'%1\'", dbusreply.signature()));
  157. return false;
  158. }
  159. const auto list = qdbus_cast<SystemdUnitFileList>(dbusreply.arguments().at(0));
  160. for (const auto &unitFile : list)
  161. {
  162. if (unitFile.path.contains(m_serviceName + ".service"))
  163. return true;
  164. }
  165. emit error(i18n("Service does not exist: \'%1\'", m_serviceName));
  166. return false;
  167. }
  168. bool SystemdCommunicator::systemdServiceActive() const
  169. {
  170. if (serviceExists() && m_serviceInterface)
  171. {
  172. if (m_serviceInterface->property("ActiveState").toString() == QStringLiteral("active"))
  173. return true;
  174. }
  175. return false;
  176. }
  177. bool SystemdCommunicator::systemdServiceEnabled() const
  178. {
  179. if (serviceExists())
  180. {
  181. QDBusReply<QString> reply = m_managerInterface->call(QDBus::AutoDetect, QStringLiteral("GetUnitFileState"), m_serviceName + ".service");
  182. if (reply.isValid())
  183. return reply.value() == QStringLiteral("enabled");
  184. else
  185. emit error(reply.error().message());
  186. }
  187. return false;
  188. }
  189. void SystemdCommunicator::setServiceEnabled(bool enabled)
  190. {
  191. if (m_serviceEnabled == enabled)
  192. return;
  193. m_serviceEnabled = enabled;
  194. emit serviceEnabledChanged();
  195. emit needsApplyChanged();
  196. }
  197. void SystemdCommunicator::setServiceActive(bool active)
  198. {
  199. if (m_serviceActive == active)
  200. return;
  201. m_serviceActive = active;
  202. emit serviceActiveChanged();
  203. emit needsApplyChanged();
  204. }
  205. bool SystemdCommunicator::restartService()
  206. {
  207. if (serviceExists())
  208. {
  209. emit info(i18n("Restarting service: \'%1\'", m_serviceName));
  210. auto args = QVariantList() << m_serviceName + ".service" << "replace";
  211. return dbusAction(QStringLiteral("ReloadOrRestartUnit"), args);
  212. }
  213. return false;
  214. }
  215. bool SystemdCommunicator::needsApply() const
  216. {
  217. return m_serviceActive != systemdServiceActive() || m_serviceEnabled != systemdServiceEnabled();
  218. }
  219. bool SystemdCommunicator::dbusAction(const QString &method, const QVariantList &arguments)
  220. {
  221. if (!m_managerInterface->isValid())
  222. {
  223. emit error(i18n("Invalid manager interface!"), true);
  224. return false;
  225. }
  226. const auto systembus = QDBusConnection::systemBus();
  227. QScopedPointer<QDBusInterface> iface(new QDBusInterface (QStringLiteral("org.freedesktop.systemd1"),
  228. QStringLiteral("/org/freedesktop/systemd1"),
  229. QStringLiteral("org.freedesktop.systemd1.Manager"),
  230. systembus,
  231. this));
  232. QDBusMessage dbusmessage;
  233. bool success = false;
  234. QString error;
  235. if (iface->isValid())
  236. {
  237. if (arguments.isEmpty())
  238. dbusmessage = iface->call(QDBus::AutoDetect, method);
  239. else
  240. dbusmessage = iface->callWithArgumentList(QDBus::AutoDetect, method, arguments);
  241. if (dbusmessage.type() == QDBusMessage::ErrorMessage)
  242. {
  243. success = false;
  244. error = dbusmessage.errorMessage();
  245. emit this->error("DBus error: " + error);
  246. }
  247. }
  248. if (success)
  249. return true;
  250. auto action = newFancontrolAction();
  251. QVariantMap map;
  252. map[QStringLiteral("action")] = "dbusaction";
  253. map[QStringLiteral("method")] = method;
  254. map[QStringLiteral("arguments")] = arguments;
  255. action.setArguments(map);
  256. const auto job = action.execute();
  257. success = job->exec();
  258. if (success)
  259. {
  260. if (method == QStringLiteral("EnableUnitFiles") || method == QStringLiteral("DisableUnitFiles"))
  261. {
  262. emit serviceEnabledChanged();
  263. emit needsApplyChanged();
  264. }
  265. else if (method == QStringLiteral("StartUnit") || method == QStringLiteral("StopUnit"))
  266. {
  267. emit serviceActiveChanged();
  268. emit needsApplyChanged();
  269. }
  270. }
  271. else
  272. emit this->error(i18n("KAuth::ExecuteJob error! Code: %1\nAdditional Info: %2", job->error(), job->errorString()), true);
  273. return success;
  274. }
  275. void SystemdCommunicator::apply(bool serviceRestart)
  276. {
  277. if (serviceExists())
  278. {
  279. if (m_serviceEnabled != systemdServiceEnabled())
  280. {
  281. QString method;
  282. if (m_serviceEnabled)
  283. {
  284. emit info(i18n("Enabling service autostart at boot:\'%1\'", m_serviceName));
  285. method = QStringLiteral("EnableUnitFiles");
  286. }
  287. else
  288. {
  289. emit info(i18n("Disabling service autostart at boot: \'%1\'", m_serviceName));
  290. method = QStringLiteral("DisableUnitFiles");
  291. }
  292. const auto files = QStringList() << m_serviceName + ".service";
  293. auto args = QVariantList() << files << false;
  294. if (m_serviceEnabled)
  295. args << true;
  296. if (!dbusAction(method, args))
  297. return;
  298. }
  299. if (m_serviceActive != systemdServiceActive())
  300. {
  301. QString method;
  302. if (m_serviceActive)
  303. {
  304. emit info(i18n("Starting service: \'%1\'", m_serviceName));
  305. method = QStringLiteral("StartUnit");
  306. }
  307. else
  308. {
  309. emit info(i18n("Stopping service: \'%1\'", m_serviceName));
  310. method = QStringLiteral("StopUnit");
  311. }
  312. auto args = QVariantList() << m_serviceName + ".service" << "replace";
  313. if (!dbusAction(method, args))
  314. return;
  315. }
  316. else if (systemdServiceActive() && m_serviceActive && serviceRestart)
  317. restartService();
  318. }
  319. }
  320. void SystemdCommunicator::reset()
  321. {
  322. setServiceActive(systemdServiceActive());
  323. setServiceEnabled(systemdServiceEnabled());
  324. }
  325. void SystemdCommunicator::updateServiceProperties(const QString&, const QVariantMap &propchanged, const QStringList&)
  326. {
  327. if (propchanged.value(QStringLiteral("ActiveState")).isValid())
  328. {
  329. emit needsApplyChanged();
  330. }
  331. if (propchanged.value(QStringLiteral("UnitFileState")).isValid())
  332. {
  333. emit needsApplyChanged();
  334. }
  335. }
  336. }