systemdcommunicator.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. /*
  2. * Copyright (C) 2015 Malte Veerman <maldela@halloarsch.de>
  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 (parent)
  64. {
  65. connect(this, &SystemdCommunicator::error, parent, &GUIBase::handleError);
  66. connect(this, &SystemdCommunicator::info, parent, &GUIBase::handleInfo);
  67. }
  68. if (serviceName.isEmpty())
  69. setServiceName(QStringLiteral(STANDARD_SERVICE_NAME));
  70. else
  71. setServiceName(serviceName);
  72. emit serviceNameChanged();
  73. m_serviceActive = systemdServiceActive();
  74. emit serviceActiveChanged();
  75. m_serviceEnabled = systemdServiceEnabled();
  76. emit serviceEnabledChanged();
  77. }
  78. void SystemdCommunicator::setServiceName(const QString &name)
  79. {
  80. if (name != m_serviceName)
  81. {
  82. if (m_serviceInterface)
  83. {
  84. QDBusConnection::systemBus().disconnect(QStringLiteral("org.freedesktop.systemd1"),
  85. m_serviceObjectPath,
  86. QStringLiteral("org.freedesktop.DBus.Properties"),
  87. QStringLiteral("PropertiesChanged"),
  88. this,
  89. SLOT(updateServiceProperties(QString, QVariantMap, QStringList)));
  90. m_serviceInterface->deleteLater();
  91. m_serviceInterface = Q_NULLPTR;
  92. }
  93. m_serviceName = name;
  94. if (serviceExists())
  95. {
  96. QVariantList arguments;
  97. arguments << QVariant(m_serviceName + ".service");
  98. const auto dbusreply = m_managerInterface->callWithArgumentList(QDBus::AutoDetect, QStringLiteral("LoadUnit"), arguments);
  99. if (dbusreply.type() == QDBusMessage::ErrorMessage)
  100. {
  101. emit error(dbusreply.errorMessage());
  102. m_serviceObjectPath.clear();
  103. }
  104. else
  105. {
  106. m_serviceObjectPath = qdbus_cast<QDBusObjectPath>(dbusreply.arguments().at(0)).path();
  107. m_serviceInterface = new QDBusInterface(QStringLiteral("org.freedesktop.systemd1"),
  108. m_serviceObjectPath,
  109. QStringLiteral("org.freedesktop.systemd1.Unit"),
  110. QDBusConnection::systemBus(),
  111. this);
  112. QDBusConnection::systemBus().connect(QStringLiteral("org.freedesktop.systemd1"),
  113. m_serviceObjectPath,
  114. QStringLiteral("org.freedesktop.DBus.Properties"),
  115. QStringLiteral("PropertiesChanged"),
  116. this,
  117. SLOT(updateServiceProperties(QString, QVariantMap, QStringList)));
  118. }
  119. }
  120. emit serviceNameChanged();
  121. emit serviceEnabledChanged();
  122. emit serviceActiveChanged();
  123. emit info(i18n("New srevice name: \'%1\'", m_serviceName));
  124. }
  125. }
  126. bool SystemdCommunicator::serviceExists() const
  127. {
  128. if (m_serviceInterface)
  129. {
  130. if (m_serviceInterface->isValid())
  131. return true;
  132. }
  133. QDBusMessage dbusreply;
  134. if (m_managerInterface && m_managerInterface->isValid())
  135. dbusreply = m_managerInterface->call(QDBus::AutoDetect, QStringLiteral("ListUnitFiles"));
  136. if (dbusreply.type() == QDBusMessage::ErrorMessage)
  137. {
  138. emit error(dbusreply.errorMessage());
  139. return false;
  140. }
  141. const auto list = qdbus_cast<SystemdUnitFileList>(dbusreply.arguments().at(0));
  142. for (const auto &unitFile : list)
  143. {
  144. if (unitFile.path.contains(m_serviceName + ".service"))
  145. return true;
  146. }
  147. emit error(i18n("Service does not exist: \'%1\'", m_serviceName));
  148. return false;
  149. }
  150. bool SystemdCommunicator::systemdServiceActive() const
  151. {
  152. if (serviceExists() && m_serviceInterface)
  153. {
  154. if (m_serviceInterface->property("ActiveState").toString() == QStringLiteral("active"))
  155. return true;
  156. }
  157. return false;
  158. }
  159. bool SystemdCommunicator::systemdServiceEnabled() const
  160. {
  161. if (serviceExists())
  162. {
  163. QDBusReply<QString> reply = m_managerInterface->call(QDBus::AutoDetect, QStringLiteral("GetUnitFileState"), m_serviceName + ".service");
  164. if (reply.isValid())
  165. return reply.value() == QStringLiteral("enabled");
  166. else
  167. emit error(reply.error().message());
  168. }
  169. return false;
  170. }
  171. void SystemdCommunicator::setServiceEnabled(bool enabled)
  172. {
  173. if (m_serviceEnabled == enabled)
  174. return;
  175. m_serviceEnabled = enabled;
  176. emit serviceEnabledChanged();
  177. emit needsApplyChanged();
  178. }
  179. void SystemdCommunicator::setServiceActive(bool active)
  180. {
  181. if (m_serviceActive == active)
  182. return;
  183. m_serviceActive = active;
  184. emit serviceActiveChanged();
  185. emit needsApplyChanged();
  186. }
  187. bool SystemdCommunicator::restartService()
  188. {
  189. if (serviceExists())
  190. {
  191. emit info(i18n("Restarting service: \'%1\'", m_serviceName));
  192. auto args = QVariantList() << m_serviceName + ".service" << "replace";
  193. return dbusAction(QStringLiteral("ReloadOrRestartUnit"), args);
  194. }
  195. emit error(i18n("Service does not exist: \'%1\'", m_serviceName));
  196. return false;
  197. }
  198. bool SystemdCommunicator::needsApply() const
  199. {
  200. return m_serviceActive != systemdServiceActive() || m_serviceEnabled != systemdServiceEnabled();
  201. }
  202. bool SystemdCommunicator::dbusAction(const QString &method, const QVariantList &arguments)
  203. {
  204. if (!m_managerInterface->isValid())
  205. {
  206. emit error(i18n("Invalid manager interface!"), true);
  207. return false;
  208. }
  209. auto action = newFancontrolAction();
  210. QVariantMap map;
  211. map[QStringLiteral("action")] = "dbusaction";
  212. map[QStringLiteral("method")] = method;
  213. map[QStringLiteral("arguments")] = arguments;
  214. action.setArguments(map);
  215. const auto job = action.execute();
  216. bool success = job->exec();
  217. if (success)
  218. {
  219. if (method == QStringLiteral("EnableUnitFiles") || method == QStringLiteral("DisableUnitFiles"))
  220. {
  221. emit serviceEnabledChanged();
  222. emit needsApplyChanged();
  223. }
  224. else if (method == QStringLiteral("StartUnit") || method == QStringLiteral("StopUnit"))
  225. {
  226. emit serviceActiveChanged();
  227. emit needsApplyChanged();
  228. }
  229. }
  230. else
  231. {
  232. emit error(i18n("Dbus error: %1", job->errorString()));
  233. }
  234. return success;
  235. }
  236. void SystemdCommunicator::apply(bool serviceRestart)
  237. {
  238. if (serviceExists())
  239. {
  240. if (m_serviceEnabled != systemdServiceEnabled())
  241. {
  242. QString method;
  243. if (m_serviceEnabled)
  244. {
  245. emit info(i18n("Enabling service autostart at boot:\'%1\'", m_serviceName));
  246. method = QStringLiteral("EnableUnitFiles");
  247. }
  248. else
  249. {
  250. emit info(i18n("Disabling service autostart at boot: \'%1\'", m_serviceName));
  251. method = QStringLiteral("DisableUnitFiles");
  252. }
  253. const auto files = QStringList() << m_serviceName + ".service";
  254. auto args = QVariantList() << files << false;
  255. if (m_serviceEnabled)
  256. args << true;
  257. if (!dbusAction(method, args))
  258. return;
  259. }
  260. if (m_serviceActive != systemdServiceActive())
  261. {
  262. QString method;
  263. if (m_serviceEnabled)
  264. {
  265. emit info(i18n("Starting service: \'%1\'", m_serviceName));
  266. method = QStringLiteral("StartUnit");
  267. }
  268. else
  269. {
  270. emit info(i18n("Stopping service: \'%1\'", m_serviceName));
  271. method = QStringLiteral("StopUnit");
  272. }
  273. auto args = QVariantList() << m_serviceName + ".service" << "replace";
  274. if (!dbusAction(method, args))
  275. return;
  276. }
  277. else if (systemdServiceActive() && m_serviceActive && serviceRestart)
  278. restartService();
  279. }
  280. }
  281. void SystemdCommunicator::reset()
  282. {
  283. setServiceActive(systemdServiceActive());
  284. setServiceEnabled(systemdServiceEnabled());
  285. }
  286. void SystemdCommunicator::updateServiceProperties(QString, QVariantMap propchanged, QStringList)
  287. {
  288. if (propchanged.value(QStringLiteral("ActiveState")).isValid())
  289. {
  290. emit needsApplyChanged();
  291. }
  292. if (propchanged.value(QStringLiteral("UnitFileState")).isValid())
  293. {
  294. emit needsApplyChanged();
  295. }
  296. }
  297. }