systemdcommunicator.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  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/QDBusReply>
  26. #include <KAuth/KAuthExecuteJob>
  27. #include <KI18n/KLocalizedString>
  28. #ifndef STANDARD_SERVICE_NAME
  29. #define STANDARD_SERVICE_NAME "fancontrol"
  30. #endif
  31. typedef struct
  32. {
  33. QString path;
  34. QString state;
  35. } SystemdUnitFile;
  36. Q_DECLARE_METATYPE(SystemdUnitFile)
  37. typedef QList<SystemdUnitFile> SystemdUnitFileList;
  38. Q_DECLARE_METATYPE(SystemdUnitFileList)
  39. QDBusArgument& operator <<(QDBusArgument &argument, const SystemdUnitFile &unitFile)
  40. {
  41. argument.beginStructure();
  42. argument << unitFile.path << unitFile.state;
  43. argument.endStructure();
  44. return argument;
  45. }
  46. const QDBusArgument& operator >>(const QDBusArgument &argument, SystemdUnitFile &unitFile)
  47. {
  48. argument.beginStructure();
  49. argument >> unitFile.path >> unitFile.state;
  50. argument.endStructure();
  51. return argument;
  52. }
  53. namespace Fancontrol
  54. {
  55. SystemdCommunicator::SystemdCommunicator(GUIBase *parent, const QString &serviceName) : QObject(parent),
  56. m_managerInterface(new QDBusInterface(QStringLiteral("org.freedesktop.systemd1"),
  57. QStringLiteral("/org/freedesktop/systemd1"),
  58. QStringLiteral("org.freedesktop.systemd1.Manager"),
  59. QDBusConnection::systemBus(),
  60. this)),
  61. m_serviceInterface(Q_NULLPTR)
  62. {
  63. if (!m_managerInterface || !m_managerInterface->isValid())
  64. emit error(i18n("Unable to init systemd dbus manager interface!"), true);
  65. if (parent)
  66. {
  67. connect(this, &SystemdCommunicator::error, parent, &GUIBase::handleError);
  68. connect(this, &SystemdCommunicator::info, parent, &GUIBase::handleInfo);
  69. }
  70. if (serviceName.isEmpty())
  71. setServiceName(QStringLiteral(STANDARD_SERVICE_NAME));
  72. else
  73. setServiceName(serviceName);
  74. m_serviceActive = systemdServiceActive();
  75. m_serviceEnabled = systemdServiceEnabled();
  76. }
  77. void SystemdCommunicator::setServiceName(const QString &name)
  78. {
  79. if (name != m_serviceName)
  80. {
  81. if (m_serviceInterface)
  82. {
  83. QDBusConnection::systemBus().disconnect(QStringLiteral("org.freedesktop.systemd1"),
  84. m_serviceObjectPath,
  85. QStringLiteral("org.freedesktop.DBus.Properties"),
  86. QStringLiteral("PropertiesChanged"),
  87. this,
  88. SLOT(updateServiceProperties(QString, QVariantMap, QStringList)));
  89. m_serviceInterface->deleteLater();
  90. m_serviceInterface = Q_NULLPTR;
  91. }
  92. m_serviceName = name;
  93. emit serviceNameChanged();
  94. emit info(i18n("New service name: \'%1\'", m_serviceName));
  95. if (serviceExists())
  96. {
  97. QVariantList arguments;
  98. arguments << QVariant(m_serviceName + ".service");
  99. const auto dbusreply = m_managerInterface->callWithArgumentList(QDBus::AutoDetect, QStringLiteral("LoadUnit"), arguments);
  100. if (dbusreply.type() == QDBusMessage::ErrorMessage)
  101. {
  102. emit error(dbusreply.errorMessage());
  103. m_serviceObjectPath.clear();
  104. }
  105. else if (dbusreply.type() == QDBusMessage::ReplyMessage)
  106. {
  107. m_serviceObjectPath = qdbus_cast<QDBusObjectPath>(dbusreply.arguments().at(0)).path();
  108. m_serviceInterface = new QDBusInterface(QStringLiteral("org.freedesktop.systemd1"),
  109. m_serviceObjectPath,
  110. QStringLiteral("org.freedesktop.systemd1.Unit"),
  111. QDBusConnection::systemBus(),
  112. this);
  113. if (!m_serviceInterface || !m_serviceInterface->isValid())
  114. emit error(i18n("Unable to init systemd dbus service interface: %1", m_serviceInterface->lastError().message()), true);
  115. QDBusConnection::systemBus().connect(QStringLiteral("org.freedesktop.systemd1"),
  116. m_serviceObjectPath,
  117. QStringLiteral("org.freedesktop.DBus.Properties"),
  118. QStringLiteral("PropertiesChanged"),
  119. this,
  120. SLOT(updateServiceProperties(QString, QVariantMap, QStringList)));
  121. }
  122. else
  123. emit error(i18n("Dbus reply message is not of type \'QDBusMessage::ReplyMessage\'"));
  124. }
  125. emit serviceEnabledChanged();
  126. emit serviceActiveChanged();
  127. emit needsApplyChanged();
  128. }
  129. }
  130. bool SystemdCommunicator::serviceExists() const
  131. {
  132. if (m_serviceInterface && m_serviceInterface->isValid())
  133. return true;
  134. QDBusMessage dbusreply;
  135. if (m_managerInterface && m_managerInterface->isValid())
  136. dbusreply = m_managerInterface->call(QDBus::AutoDetect, QStringLiteral("ListUnitFiles"));
  137. else
  138. {
  139. emit error(i18n("Systemd dbus manager interface not initialized!"), true);
  140. return false;
  141. }
  142. if (dbusreply.type() == QDBusMessage::ErrorMessage)
  143. {
  144. emit error(dbusreply.errorMessage());
  145. return false;
  146. }
  147. else if (dbusreply.type() == QDBusMessage::InvalidMessage)
  148. {
  149. emit error(i18n("Dbus returned invalid answer"));
  150. return false;
  151. }
  152. else if (dbusreply.signature() != QStringLiteral("a(ss)"))
  153. {
  154. emit error(i18n("Dbus returned answer with wrong signature: \'%1\'", dbusreply.signature()));
  155. return false;
  156. }
  157. const auto list = qdbus_cast<SystemdUnitFileList>(dbusreply.arguments().at(0));
  158. for (const auto &unitFile : list)
  159. {
  160. if (unitFile.path.contains(m_serviceName + ".service"))
  161. return true;
  162. }
  163. emit error(i18n("Service does not exist: \'%1\'", m_serviceName));
  164. return false;
  165. }
  166. bool SystemdCommunicator::systemdServiceActive() const
  167. {
  168. if (serviceExists() && m_serviceInterface)
  169. {
  170. if (m_serviceInterface->property("ActiveState").toString() == QStringLiteral("active"))
  171. return true;
  172. }
  173. return false;
  174. }
  175. bool SystemdCommunicator::systemdServiceEnabled() const
  176. {
  177. if (serviceExists())
  178. {
  179. QDBusReply<QString> reply = m_managerInterface->call(QDBus::AutoDetect, QStringLiteral("GetUnitFileState"), m_serviceName + ".service");
  180. if (reply.isValid())
  181. return reply.value() == QStringLiteral("enabled");
  182. else
  183. emit error(reply.error().message());
  184. }
  185. return false;
  186. }
  187. void SystemdCommunicator::setServiceEnabled(bool enabled)
  188. {
  189. if (m_serviceEnabled == enabled)
  190. return;
  191. m_serviceEnabled = enabled;
  192. emit serviceEnabledChanged();
  193. emit needsApplyChanged();
  194. }
  195. void SystemdCommunicator::setServiceActive(bool active)
  196. {
  197. if (m_serviceActive == active)
  198. return;
  199. m_serviceActive = active;
  200. emit serviceActiveChanged();
  201. emit needsApplyChanged();
  202. }
  203. bool SystemdCommunicator::restartService()
  204. {
  205. if (serviceExists())
  206. {
  207. emit info(i18n("Restarting service: \'%1\'", m_serviceName));
  208. auto args = QVariantList() << m_serviceName + ".service" << "replace";
  209. return dbusAction(QStringLiteral("ReloadOrRestartUnit"), args);
  210. }
  211. return false;
  212. }
  213. bool SystemdCommunicator::needsApply() const
  214. {
  215. return m_serviceActive != systemdServiceActive() || m_serviceEnabled != systemdServiceEnabled();
  216. }
  217. bool SystemdCommunicator::dbusAction(const QString &method, const QVariantList &arguments)
  218. {
  219. if (!m_managerInterface->isValid())
  220. {
  221. emit error(i18n("Invalid manager interface!"), true);
  222. return false;
  223. }
  224. auto action = newFancontrolAction();
  225. QVariantMap map;
  226. map[QStringLiteral("action")] = "dbusaction";
  227. map[QStringLiteral("method")] = method;
  228. map[QStringLiteral("arguments")] = arguments;
  229. action.setArguments(map);
  230. const auto job = action.execute();
  231. bool success = job->exec();
  232. if (success)
  233. {
  234. if (method == QStringLiteral("EnableUnitFiles") || method == QStringLiteral("DisableUnitFiles"))
  235. {
  236. emit serviceEnabledChanged();
  237. emit needsApplyChanged();
  238. }
  239. else if (method == QStringLiteral("StartUnit") || method == QStringLiteral("StopUnit"))
  240. {
  241. emit serviceActiveChanged();
  242. emit needsApplyChanged();
  243. }
  244. }
  245. else
  246. emit error(i18n("Dbus error: %1", job->errorString()));
  247. return success;
  248. }
  249. void SystemdCommunicator::apply(bool serviceRestart)
  250. {
  251. if (serviceExists())
  252. {
  253. if (m_serviceEnabled != systemdServiceEnabled())
  254. {
  255. QString method;
  256. if (m_serviceEnabled)
  257. {
  258. emit info(i18n("Enabling service autostart at boot:\'%1\'", m_serviceName));
  259. method = QStringLiteral("EnableUnitFiles");
  260. }
  261. else
  262. {
  263. emit info(i18n("Disabling service autostart at boot: \'%1\'", m_serviceName));
  264. method = QStringLiteral("DisableUnitFiles");
  265. }
  266. const auto files = QStringList() << m_serviceName + ".service";
  267. auto args = QVariantList() << files << false;
  268. if (m_serviceEnabled)
  269. args << true;
  270. if (!dbusAction(method, args))
  271. return;
  272. }
  273. if (m_serviceActive != systemdServiceActive())
  274. {
  275. QString method;
  276. if (m_serviceActive)
  277. {
  278. emit info(i18n("Starting service: \'%1\'", m_serviceName));
  279. method = QStringLiteral("StartUnit");
  280. }
  281. else
  282. {
  283. emit info(i18n("Stopping service: \'%1\'", m_serviceName));
  284. method = QStringLiteral("StopUnit");
  285. }
  286. auto args = QVariantList() << m_serviceName + ".service" << "replace";
  287. if (!dbusAction(method, args))
  288. return;
  289. }
  290. else if (systemdServiceActive() && m_serviceActive && serviceRestart)
  291. restartService();
  292. }
  293. }
  294. void SystemdCommunicator::reset()
  295. {
  296. setServiceActive(systemdServiceActive());
  297. setServiceEnabled(systemdServiceEnabled());
  298. }
  299. void SystemdCommunicator::updateServiceProperties(const QString&, const QVariantMap &propchanged, const QStringList&)
  300. {
  301. if (propchanged.value(QStringLiteral("ActiveState")).isValid())
  302. {
  303. emit needsApplyChanged();
  304. }
  305. if (propchanged.value(QStringLiteral("UnitFileState")).isValid())
  306. {
  307. emit needsApplyChanged();
  308. }
  309. }
  310. }