loader.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843
  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 "loader.h"
  20. #include "guibase.h"
  21. #include "hwmon.h"
  22. #include "pwmfan.h"
  23. #include "fan.h"
  24. #include "fancontrolaction.h"
  25. #include <QtCore/QFile>
  26. #include <QtCore/QDir>
  27. #include <QtCore/QTextStream>
  28. #include <QtCore/QTimer>
  29. #include <QtCore/QProcess>
  30. #include <QtCore/QDebug>
  31. #include <KAuth/KAuthExecuteJob>
  32. #include <KI18n/KLocalizedString>
  33. #define HWMON_PATH "/sys/class/hwmon"
  34. #ifndef STANDARD_CONFIG_FILE
  35. #define STANDARD_CONFIG_FILE "/etc/fancontrol"
  36. #endif
  37. namespace Fancontrol
  38. {
  39. Loader::Loader(GUIBase *parent) : QObject(parent),
  40. m_reactivateAfterTesting(true),
  41. m_interval(10),
  42. m_configUrl(QUrl::fromLocalFile(QStringLiteral(STANDARD_CONFIG_FILE))),
  43. m_timer(new QTimer(this)),
  44. m_sensorsDetected(false)
  45. {
  46. if (parent)
  47. connect(this, &Loader::error, parent, &GUIBase::handleError);
  48. m_timer->setSingleShot(false);
  49. m_timer->start(1000);
  50. connect(m_timer, &QTimer::timeout, this, &Loader::sensorsUpdateNeeded);
  51. }
  52. void Loader::parseHwmons()
  53. {
  54. const auto hwmonDir = QDir(QStringLiteral(HWMON_PATH));
  55. QStringList list;
  56. if (hwmonDir.isReadable())
  57. list = hwmonDir.entryList(QDir::AllEntries | QDir::NoDotAndDotDot);
  58. else if (hwmonDir.exists())
  59. {
  60. emit error(i18n("File is not readable: \"%1\"", QStringLiteral(HWMON_PATH)), true);
  61. return;
  62. }
  63. else
  64. {
  65. emit error(i18n("File does not exist: \"%1\"", QStringLiteral(HWMON_PATH)), true);
  66. return;
  67. }
  68. QStringList dereferencedList;
  69. while (!list.isEmpty())
  70. dereferencedList << QFile::symLinkTarget(hwmonDir.absoluteFilePath(list.takeFirst()));
  71. for (const auto &hwmon : m_hwmons)
  72. {
  73. if (!dereferencedList.contains(hwmon->path()))
  74. {
  75. hwmon->deleteLater();
  76. m_hwmons.removeOne(hwmon);
  77. emit hwmonsChanged();
  78. }
  79. else
  80. hwmon->initialize();
  81. }
  82. for (const auto &hwmonPath : dereferencedList)
  83. {
  84. auto hwmonExists = false;
  85. for (const auto &hwmon : m_hwmons)
  86. {
  87. if (hwmon->path() == hwmonPath)
  88. {
  89. hwmonExists = true;
  90. break;
  91. }
  92. }
  93. if (!hwmonExists)
  94. {
  95. auto newHwmon = new Hwmon(hwmonPath, this);
  96. if (newHwmon->isValid())
  97. {
  98. connect(this, &Loader::sensorsUpdateNeeded, newHwmon, &Hwmon::sensorsUpdateNeeded);
  99. m_hwmons << newHwmon;
  100. emit hwmonsChanged();
  101. }
  102. else
  103. delete newHwmon;
  104. }
  105. }
  106. }
  107. PwmFan * Loader::pwmFan(int hwmonIndex, int pwmFanIndex) const
  108. {
  109. const auto hwmon = m_hwmons.value(hwmonIndex, Q_NULLPTR);
  110. if (!hwmon)
  111. return Q_NULLPTR;
  112. return hwmon->pwmFan(pwmFanIndex);
  113. }
  114. Temp * Loader::temp(int hwmonIndex, int tempIndex) const
  115. {
  116. const auto hwmon = m_hwmons.value(hwmonIndex, Q_NULLPTR);
  117. if (!hwmon)
  118. return Q_NULLPTR;
  119. return hwmon->temp(tempIndex);
  120. }
  121. Fan * Loader::fan(int hwmonIndex, int fanIndex) const
  122. {
  123. const auto hwmon = m_hwmons.value(hwmonIndex, Q_NULLPTR);
  124. if (!hwmon)
  125. return Q_NULLPTR;
  126. return hwmon->fan(fanIndex);
  127. }
  128. QPair<int, int> Loader::getEntryNumbers(const QString &entry)
  129. {
  130. if (entry.isEmpty())
  131. return QPair<int, int>(-1, -1);
  132. auto list = entry.split('/', QString::SkipEmptyParts);
  133. if (list.size() != 2)
  134. {
  135. emit error(i18n("Invalid entry: \"%1\"", entry));
  136. return QPair<int, int>(-1, -1);
  137. }
  138. auto &hwmon = list[0];
  139. auto &sensor = list[1];
  140. if (!hwmon.startsWith(QStringLiteral("hwmon")))
  141. {
  142. emit error(i18n("Invalid entry: \"%1\"", entry));
  143. return QPair<int, int>(-1, -1);
  144. }
  145. if (!sensor.contains(QRegExp("^(pwm|fan|temp)\\d+")))
  146. {
  147. emit error(i18n("Invalid entry: \"%1\"", entry));
  148. return QPair<int, int>(-1, -1);
  149. }
  150. auto success = false;
  151. hwmon.remove(QStringLiteral("hwmon"));
  152. sensor.remove(QRegExp("^(pwm|fan|temp)"));
  153. sensor.remove(QStringLiteral("_input"));
  154. const auto hwmonResult = hwmon.toInt(&success);
  155. if (!success)
  156. {
  157. emit error(i18n("Invalid entry: \"%1\"", entry));
  158. return QPair<int, int>(-1, -1);
  159. }
  160. const auto sensorResult = sensor.toInt(&success);
  161. if (!success)
  162. {
  163. emit error(i18n("Invalid entry: \"%1\"", entry));
  164. return QPair<int, int>(-1, -1);
  165. }
  166. return QPair<int, int>(hwmonResult, sensorResult - 1);
  167. }
  168. bool Loader::parseConfig(QString config)
  169. {
  170. //Disconnect hwmons for performance reasons
  171. //They get reconnected later
  172. for (const auto &hwmon : m_hwmons)
  173. {
  174. disconnect(hwmon, &Hwmon::configUpdateNeeded, this, &Loader::updateConfig);
  175. const auto pwmFans = hwmon->pwmFans();
  176. for (const auto &pwmFan : pwmFans)
  177. {
  178. qobject_cast<PwmFan *>(pwmFan)->reset();
  179. }
  180. }
  181. reset();
  182. bool success = true;
  183. QTextStream stream;
  184. stream.setString(&config, QIODevice::ReadOnly);
  185. QStringList lines;
  186. do
  187. {
  188. auto line(stream.readLine());
  189. if (line.startsWith('#') || line.trimmed().isEmpty())
  190. continue;
  191. const auto offset = line.indexOf('#');
  192. if (offset != -1)
  193. line.truncate(offset);
  194. line = line.simplified();
  195. lines << line;
  196. }
  197. while (!stream.atEnd());
  198. for (auto line : lines)
  199. {
  200. if (line.startsWith(QStringLiteral("INTERVAL=")))
  201. {
  202. line.remove(QStringLiteral("INTERVAL="));
  203. line = line.simplified();
  204. auto intSuccess = false;
  205. const auto interval = line.toInt(&intSuccess);
  206. if (intSuccess)
  207. setInterval(interval, false);
  208. else
  209. {
  210. emit error(i18n("Unable to parse interval line: \"%1\"", line), true);
  211. success = false;
  212. }
  213. }
  214. else if (line.startsWith(QStringLiteral("FCTEMPS=")))
  215. {
  216. line.remove(QStringLiteral("FCTEMPS="));
  217. line = line.simplified();
  218. const auto fctemps = line.split(' ');
  219. for (const auto &fctemp : fctemps)
  220. {
  221. const auto nameValuePair = fctemp.split('=');
  222. if (nameValuePair.size() == 2)
  223. {
  224. const auto pwmFanString = nameValuePair.at(0);
  225. const auto tempString = nameValuePair.at(1);
  226. const auto pwmPointer = pwmFan(getEntryNumbers(pwmFanString));
  227. const auto tempPointer = temp(getEntryNumbers(tempString));
  228. if (pwmPointer && tempPointer)
  229. {
  230. pwmPointer->setTemp(tempPointer);
  231. pwmPointer->setHasTemp(true);
  232. pwmPointer->setMinPwm(0);
  233. }
  234. else
  235. {
  236. if (!pwmPointer)
  237. emit error(i18n("Invalid fan entry: \"%1\"", pwmFanString), true);
  238. if (!tempPointer)
  239. emit error(i18n("Invalid temp entry: \"%1\"", tempString), true);
  240. }
  241. }
  242. else
  243. emit error(i18n("Invalid entry: \"%1\"", fctemp), true);
  244. }
  245. }
  246. else if (line.startsWith(QStringLiteral("DEVNAME=")))
  247. {
  248. line.remove(QStringLiteral("DEVNAME="));
  249. line = line.simplified();
  250. const auto devnames = line.split(' ');
  251. for (const auto &devname : devnames)
  252. {
  253. const auto indexNamePair = devname.split('=');
  254. if (indexNamePair.size() == 2)
  255. {
  256. auto index = indexNamePair.at(0);
  257. const auto &name = indexNamePair[1];
  258. auto intSuccess = false;
  259. index.remove(QStringLiteral("hwmon"));
  260. const auto hwmonPointer = m_hwmons.value(index.toInt(&intSuccess), Q_NULLPTR);
  261. if (!intSuccess)
  262. {
  263. emit error(i18n("Invalid DEVNAME: \"%1\"!", devname), true);
  264. success = false;
  265. }
  266. if (!hwmonPointer)
  267. {
  268. emit error(i18n("Invalid DEVNAME: \"%1\"! No hwmon with index %2", devname, index), true);
  269. success = false;
  270. }
  271. else if (hwmonPointer->name().split('.').first() != name)
  272. {
  273. emit error(i18n("Wrong name for hwmon %1! Should be \"%2\"", index, hwmonPointer->name().split('.').first()), true);
  274. success = false;
  275. }
  276. }
  277. else
  278. emit error(i18n("Invalid DEVNAME: \"%1\"!", devname), true);
  279. }
  280. }
  281. else if (line.startsWith(QStringLiteral("MINTEMP=")))
  282. {
  283. line.remove(QStringLiteral("MINTEMP="));
  284. parseConfigLine(line.simplified(), &PwmFan::setMinTemp);
  285. }
  286. else if (line.startsWith(QStringLiteral("MAXTEMP=")))
  287. {
  288. line.remove(QStringLiteral("MAXTEMP="));
  289. parseConfigLine(line.simplified(), &PwmFan::setMaxTemp);
  290. }
  291. else if (line.startsWith(QStringLiteral("MINSTART=")))
  292. {
  293. line.remove(QStringLiteral("MINSTART="));
  294. parseConfigLine(line.simplified(), &PwmFan::setMinStart);
  295. }
  296. else if (line.startsWith(QStringLiteral("MINSTOP=")))
  297. {
  298. line.remove(QStringLiteral("MINSTOP="));
  299. parseConfigLine(line.simplified(), &PwmFan::setMinStop);
  300. }
  301. else if (line.startsWith(QStringLiteral("MINPWM=")))
  302. {
  303. line.remove(QStringLiteral("MINPWM="));
  304. parseConfigLine(line.simplified(), &PwmFan::setMinPwm);
  305. }
  306. else if (line.startsWith(QStringLiteral("MAXPWM=")))
  307. {
  308. line.remove(QStringLiteral("MAXPWM="));
  309. parseConfigLine(line.simplified(), &PwmFan::setMaxPwm);
  310. }
  311. else if (!line.startsWith(QStringLiteral("DEVPATH=")) &&
  312. !line.startsWith(QStringLiteral("FCFANS=")))
  313. {
  314. emit error(i18n("Unrecognized line in config: \"%1\"", line), true);
  315. success = false;
  316. }
  317. }
  318. updateConfig();
  319. //Connect hwmons again
  320. for (const auto &hwmon : m_hwmons)
  321. connect(hwmon, &Hwmon::configUpdateNeeded, this, &Loader::updateConfig);
  322. return success;
  323. }
  324. void Loader::parseConfigLine(const QString &line, void (PwmFan::*memberSetFunction)(int))
  325. {
  326. if (!memberSetFunction)
  327. return;
  328. const auto entries = line.split(' ');
  329. for (const auto &entry : entries)
  330. {
  331. const auto fanValuePair = entry.split('=');
  332. if (fanValuePair.size() == 2)
  333. {
  334. const auto pwmFanString = fanValuePair.at(0);
  335. const auto valueString = fanValuePair.at(1);
  336. auto success = false;
  337. const auto value = valueString.toInt(&success);
  338. if (success)
  339. {
  340. auto pwmFanPointer = pwmFan(getEntryNumbers(pwmFanString));
  341. if (pwmFanPointer)
  342. (pwmFanPointer->*memberSetFunction)(value);
  343. else
  344. emit error(i18n("Invalid fan entry: \"%1\"", pwmFanString), true);
  345. }
  346. else
  347. emit error(i18n("%1 is not an integer!", valueString));
  348. }
  349. else
  350. emit error(i18n("Invalid entry to parse: \"%1\"", entry));
  351. }
  352. }
  353. bool Loader::load(const QUrl &url)
  354. {
  355. QString fileName;
  356. if (url.isEmpty())
  357. {
  358. // qDebug() << "Given empty url. Fallback to" << m_configUrl;
  359. fileName = m_configUrl.toLocalFile();
  360. }
  361. else if (url.isValid())
  362. {
  363. if (url.isLocalFile())
  364. fileName = url.toLocalFile();
  365. else
  366. {
  367. emit error(i18n("%1 is not a local file!", url.toDisplayString()));
  368. return false;
  369. }
  370. }
  371. else
  372. {
  373. emit error(i18n("%1 is not a valid url!", url.toDisplayString()));
  374. return false;
  375. }
  376. QTextStream stream;
  377. QFile file(fileName);
  378. QString fileContent;
  379. if (file.open(QFile::ReadOnly | QFile::Text))
  380. {
  381. stream.setDevice(&file);
  382. fileContent = stream.readAll();
  383. }
  384. else if (file.exists())
  385. {
  386. auto action = newFancontrolAction();
  387. if (action.isValid())
  388. {
  389. auto map = QVariantMap();
  390. map[QStringLiteral("action")] = QVariant("read");
  391. map[QStringLiteral("filename")] = fileName;
  392. action.setArguments(map);
  393. auto reply = action.execute();
  394. if (!reply->exec())
  395. {
  396. if (reply->error() == 4)
  397. {
  398. qDebug() << "Loading of file aborted by user";
  399. return false;
  400. }
  401. emit error(reply->errorString() + reply->errorText(), true);
  402. return false;
  403. }
  404. else
  405. fileContent = reply->data().value(QStringLiteral("content")).toString();
  406. }
  407. else
  408. emit error(i18n("Action not supported! Try running the application as root."), true);
  409. }
  410. else
  411. {
  412. emit error(i18n("File does not exist: \"%1\"" ,fileName));
  413. return false;
  414. }
  415. auto success = parseConfig(fileContent);
  416. if (!url.isEmpty())
  417. {
  418. m_configUrl = url;
  419. emit configUrlChanged();
  420. }
  421. return success;
  422. }
  423. bool Loader::save(const QUrl &url)
  424. {
  425. QString fileName;
  426. if (url.isEmpty())
  427. {
  428. // qDebug() << "Given empty url. Fallback to " << m_configUrl;
  429. fileName = m_configUrl.toLocalFile();
  430. }
  431. else if (url.isLocalFile())
  432. fileName = url.toLocalFile();
  433. else
  434. {
  435. emit error(i18n("%1 is not a local file!", url.toDisplayString()), true);
  436. return false;
  437. }
  438. QFile file(fileName);
  439. if (file.open(QFile::WriteOnly | QFile::Text))
  440. {
  441. QTextStream stream(&file);
  442. stream << m_configFile;
  443. }
  444. else
  445. {
  446. auto action = newFancontrolAction();
  447. if (action.isValid())
  448. {
  449. QVariantMap map;
  450. map[QStringLiteral("action")] = QVariant("write");
  451. map[QStringLiteral("filename")] = fileName;
  452. map[QStringLiteral("content")] = m_configFile;
  453. action.setArguments(map);
  454. auto reply = action.execute();
  455. if (!reply->exec())
  456. {
  457. if (reply->error() == 4)
  458. {
  459. qDebug() << "Saving of file aborted by user";
  460. return false;
  461. }
  462. emit error(reply->errorString() + reply->errorText(), true);
  463. return false;
  464. }
  465. }
  466. else
  467. emit error(i18n("Action not supported! Try running the application as root."), true);
  468. }
  469. return true;
  470. }
  471. void Loader::updateConfig()
  472. {
  473. const auto configFile = createConfig();
  474. if (configFile != m_configFile)
  475. {
  476. m_configFile = configFile;
  477. emit configFileChanged();
  478. }
  479. }
  480. QString Loader::createConfig() const
  481. {
  482. QList<Hwmon *> usedHwmons;
  483. QList<PwmFan *> usedFans;
  484. for (const auto &hwmon : m_hwmons)
  485. {
  486. if (hwmon->pwmFans().size() > 0 && !usedHwmons.contains(hwmon))
  487. usedHwmons << hwmon;
  488. const auto pwmFans = hwmon->pwmFans();
  489. for (const auto &pwmFan : pwmFans)
  490. {
  491. if (pwmFan->hasTemp() && pwmFan->temp() && !pwmFan->testing())
  492. {
  493. usedFans << pwmFan;
  494. if (!usedHwmons.contains(pwmFan->temp()->parent()))
  495. usedHwmons << pwmFan->temp()->parent();
  496. }
  497. }
  498. }
  499. auto configFile = QStringLiteral("# This file was created by Fancontrol-GUI") + QChar(QChar::LineFeed);
  500. if (m_interval != 0)
  501. configFile += "INTERVAL=" + QString::number(m_interval) + QChar(QChar::LineFeed);
  502. if (!usedHwmons.isEmpty())
  503. {
  504. configFile += "DEVPATH=";
  505. for (const auto &hwmon : usedHwmons)
  506. {
  507. auto sanitizedPath = hwmon->path();
  508. sanitizedPath.remove(QRegExp("^/sys/"));
  509. sanitizedPath.remove(QRegExp("/hwmon/hwmon\\d\\s*$"));
  510. configFile += "hwmon" + QString::number(hwmon->index()) + "=" + sanitizedPath + QChar(QChar::Space);
  511. }
  512. configFile += QChar(QChar::LineFeed);
  513. configFile += "DEVNAME=";
  514. for (const auto &hwmon : usedHwmons)
  515. configFile += "hwmon" + QString::number(hwmon->index()) + "=" + hwmon->name().split('.').first() + QChar(QChar::Space);
  516. configFile += QChar(QChar::LineFeed);
  517. if (!usedFans.isEmpty())
  518. {
  519. configFile += "FCTEMPS=";
  520. for (const auto &pwmFan : usedFans)
  521. {
  522. configFile += "hwmon" + QString::number(pwmFan->parent()->index()) + "/";
  523. configFile += "pwm" + QString::number(pwmFan->index()) + "=";
  524. configFile += "hwmon" + QString::number(pwmFan->temp()->parent()->index()) + "/";
  525. configFile += "temp" + QString::number(pwmFan->temp()->index()) + "_input ";
  526. }
  527. configFile += QChar(QChar::LineFeed);
  528. configFile += "FCFANS=";
  529. for (const auto &pwmFan : usedFans)
  530. {
  531. configFile += "hwmon" + QString::number(pwmFan->parent()->index()) + "/";
  532. configFile += "pwm" + QString::number(pwmFan->index()) + "=";
  533. configFile += "hwmon" + QString::number(pwmFan->parent()->index()) + "/";
  534. configFile += "fan" + QString::number(pwmFan->index()) + "_input ";
  535. }
  536. configFile += QChar(QChar::LineFeed);
  537. configFile += "MINTEMP=";
  538. for (const auto &pwmFan : usedFans)
  539. {
  540. configFile += "hwmon" + QString::number(pwmFan->parent()->index()) + "/";
  541. configFile += "pwm" + QString::number(pwmFan->index()) + "=";
  542. configFile += QString::number(pwmFan->minTemp()) + QChar(QChar::Space);
  543. }
  544. configFile += QChar(QChar::LineFeed);
  545. configFile += "MAXTEMP=";
  546. for (const auto &pwmFan : usedFans)
  547. {
  548. configFile += "hwmon" + QString::number(pwmFan->parent()->index()) + "/";
  549. configFile += "pwm" + QString::number(pwmFan->index()) + "=";
  550. configFile += QString::number(pwmFan->maxTemp()) + QChar(QChar::Space);
  551. }
  552. configFile += QChar(QChar::LineFeed);
  553. configFile += "MINSTART=";
  554. for (const auto &pwmFan : usedFans)
  555. {
  556. configFile += "hwmon" + QString::number(pwmFan->parent()->index()) + "/";
  557. configFile += "pwm" + QString::number(pwmFan->index()) + "=";
  558. configFile += QString::number(pwmFan->minStart()) + QChar(QChar::Space);
  559. }
  560. configFile += QChar(QChar::LineFeed);
  561. configFile += "MINSTOP=";
  562. for (const auto &pwmFan : usedFans)
  563. {
  564. configFile += "hwmon" + QString::number(pwmFan->parent()->index()) + "/";
  565. configFile += "pwm" + QString::number(pwmFan->index()) + "=";
  566. configFile += QString::number(pwmFan->minStop()) + QChar(QChar::Space);
  567. }
  568. configFile += QChar(QChar::LineFeed);
  569. configFile += "MINPWM=";
  570. for (const auto &pwmFan : usedFans)
  571. {
  572. configFile += "hwmon" + QString::number(pwmFan->parent()->index()) + "/";
  573. configFile += "pwm" + QString::number(pwmFan->index()) + "=";
  574. configFile += QString::number(pwmFan->minPwm()) + QChar(QChar::Space);
  575. }
  576. configFile += QChar(QChar::LineFeed);
  577. configFile += "MAXPWM=";
  578. for (const auto &pwmFan : usedFans)
  579. {
  580. configFile += "hwmon" + QString::number(pwmFan->parent()->index()) + "/";
  581. configFile += "pwm" + QString::number(pwmFan->index()) + "=";
  582. configFile += QString::number(pwmFan->maxPwm()) + QChar(QChar::Space);
  583. }
  584. configFile += QChar(QChar::LineFeed);
  585. }
  586. }
  587. return configFile;
  588. }
  589. void Loader::setInterval(int interval, bool writeNewConfig)
  590. {
  591. if (interval < 1)
  592. {
  593. emit error(i18n("Interval must be greater or equal to one!"), true);
  594. return;
  595. }
  596. if (interval != m_interval)
  597. {
  598. m_interval = interval;
  599. emit intervalChanged();
  600. if (writeNewConfig)
  601. updateConfig();
  602. }
  603. }
  604. void Loader::testFans()
  605. {
  606. for (const auto &hwmon : m_hwmons)
  607. hwmon->testFans();
  608. }
  609. void Loader::abortTestingFans()
  610. {
  611. for (const auto &hwmon : m_hwmons)
  612. hwmon->abortTestingFans();
  613. }
  614. void Loader::detectSensors()
  615. {
  616. auto program = QStringLiteral("sensors-detect");
  617. auto arguments = QStringList() << QStringLiteral("--auto");
  618. auto process = new QProcess(this);
  619. process->start(program, arguments);
  620. connect(process, static_cast<void(QProcess::*)(int)>(&QProcess::finished),
  621. this, static_cast<void(Loader::*)(int)>(&Loader::handleDetectSensorsResult));
  622. }
  623. void Loader::handleDetectSensorsResult(int exitCode)
  624. {
  625. auto process = qobject_cast<QProcess *>(sender());
  626. if (exitCode)
  627. {
  628. if (process)
  629. emit error(process->readAllStandardOutput());
  630. auto action = newFancontrolAction();
  631. if (action.isValid())
  632. {
  633. QVariantMap map;
  634. map[QStringLiteral("action")] = QVariant("detectSensors");
  635. action.setArguments(map);
  636. auto job = action.execute();
  637. connect(job, &KAuth::ExecuteJob::result, this, static_cast<void(Loader::*)(KJob *)>(&Loader::handleDetectSensorsResult));
  638. job->start();
  639. }
  640. else
  641. emit error(i18n("Action not supported! Try running the application as root."), true);
  642. }
  643. else
  644. {
  645. if (!m_sensorsDetected)
  646. {
  647. m_sensorsDetected = true;
  648. emit sensorsDetectedChanged();
  649. }
  650. parseHwmons();
  651. }
  652. if (process)
  653. process->deleteLater();
  654. }
  655. void Loader::handleDetectSensorsResult(KJob *job)
  656. {
  657. if (job->error())
  658. {
  659. if (job->error() == 4)
  660. {
  661. // qDebug() << "Aborted by user";
  662. return;
  663. }
  664. emit error(job->errorString() + job->errorText(), true);
  665. }
  666. else
  667. {
  668. if (!m_sensorsDetected)
  669. {
  670. m_sensorsDetected = true;
  671. emit sensorsDetectedChanged();
  672. }
  673. parseHwmons();
  674. }
  675. }
  676. QList<QObject *> Loader::hwmonsAsObjects() const
  677. {
  678. auto list = QList<QObject *>();
  679. for (const auto &hwmon : m_hwmons)
  680. list << qobject_cast<QObject *>(hwmon);
  681. return list;
  682. }
  683. void Loader::handleTestStatusChanged()
  684. {
  685. auto testing = false;
  686. for (const auto &hwmon : m_hwmons)
  687. {
  688. if (hwmon->testing() == true)
  689. {
  690. testing = true;
  691. break;
  692. }
  693. }
  694. if (!testing && !m_reactivateAfterTesting)
  695. return;
  696. emit requestSetServiceActive(!testing);
  697. }
  698. void Loader::setRestartServiceAfterTesting(bool restart)
  699. {
  700. if (m_reactivateAfterTesting == restart)
  701. return;
  702. m_reactivateAfterTesting = restart;
  703. emit restartServiceAfterTestingChanged();
  704. }
  705. void Loader::reset() const
  706. {
  707. for (const auto &hwmon : m_hwmons)
  708. hwmon->reset();
  709. }
  710. }