loader.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863
  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. QTextStream stream;
  183. stream.setString(&config, QIODevice::ReadOnly);
  184. QStringList lines;
  185. do
  186. {
  187. auto line(stream.readLine());
  188. if (line.startsWith('#') || line.trimmed().isEmpty())
  189. continue;
  190. const auto offset = line.indexOf('#');
  191. if (offset != -1)
  192. line.truncate(offset);
  193. line = line.simplified();
  194. lines << line;
  195. }
  196. while (!stream.atEnd());
  197. for (auto line : lines)
  198. {
  199. if (line.startsWith(QStringLiteral("INTERVAL=")))
  200. {
  201. line.remove(QStringLiteral("INTERVAL="));
  202. line = line.simplified();
  203. auto success = false;
  204. const auto interval = line.toInt(&success);
  205. if (success)
  206. setInterval(interval, false);
  207. else
  208. {
  209. //Connect hwmons again
  210. for (const auto &hwmon : m_hwmons)
  211. connect(hwmon, &Hwmon::configUpdateNeeded, this, &Loader::updateConfig);
  212. emit error(i18n("Unable to parse interval line: \"%1\"", line), true);
  213. return false;
  214. }
  215. }
  216. else if (line.startsWith(QStringLiteral("FCTEMPS=")))
  217. {
  218. line.remove(QStringLiteral("FCTEMPS="));
  219. line = line.simplified();
  220. const auto fctemps = line.split(' ');
  221. for (const auto &fctemp : fctemps)
  222. {
  223. const auto nameValuePair = fctemp.split('=');
  224. if (nameValuePair.size() == 2)
  225. {
  226. const auto pwmFanString = nameValuePair.at(0);
  227. const auto tempString = nameValuePair.at(1);
  228. const auto pwmPointer = pwmFan(getEntryNumbers(pwmFanString));
  229. const auto tempPointer = temp(getEntryNumbers(tempString));
  230. if (pwmPointer && tempPointer)
  231. {
  232. pwmPointer->setTemp(tempPointer);
  233. pwmPointer->setHasTemp(true);
  234. pwmPointer->setMinPwm(0);
  235. }
  236. else
  237. {
  238. if (!pwmPointer)
  239. emit error(i18n("Invalid fan entry: \"%1\"", pwmFanString), true);
  240. if (!tempPointer)
  241. emit error(i18n("Invalid temp entry: \"%1\"", tempString), true);
  242. }
  243. }
  244. else
  245. emit error(i18n("Invalid entry: \"%1\"", fctemp), true);
  246. }
  247. }
  248. else if (line.startsWith(QStringLiteral("DEVNAME=")))
  249. {
  250. line.remove(QStringLiteral("DEVNAME="));
  251. line = line.simplified();
  252. const auto devnames = line.split(' ');
  253. for (const auto &devname : devnames)
  254. {
  255. const auto indexNamePair = devname.split('=');
  256. if (indexNamePair.size() == 2)
  257. {
  258. auto index = indexNamePair.at(0);
  259. const auto &name = indexNamePair[1];
  260. auto success = false;
  261. index.remove(QStringLiteral("hwmon"));
  262. const auto hwmonPointer = m_hwmons.value(index.toInt(&success), Q_NULLPTR);
  263. if (!success)
  264. {
  265. //Connect hwmons again
  266. for (const auto &hwmon : m_hwmons)
  267. connect(hwmon, &Hwmon::configUpdateNeeded, this, &Loader::updateConfig);
  268. emit error(i18n("Invalid DEVNAME: \"%1\"!", devname), true);
  269. return false;
  270. }
  271. if (!hwmonPointer)
  272. {
  273. //Connect hwmons again
  274. for (const auto &hwmon : m_hwmons)
  275. connect(hwmon, &Hwmon::configUpdateNeeded, this, &Loader::updateConfig);
  276. emit error(i18n("Invalid DEVNAME: \"%1\"! No hwmon with index %2", devname, index), true);
  277. return false;
  278. }
  279. if (hwmonPointer->name().split('.').first() != name)
  280. {
  281. //Connect hwmons again
  282. for (const auto &hwmon : m_hwmons)
  283. connect(hwmon, &Hwmon::configUpdateNeeded, this, &Loader::updateConfig);
  284. emit error(i18n("Wrong name for hwmon %1! Should be \"%2\"", index, hwmonPointer->name().split('.').first()), true);
  285. return false;
  286. }
  287. }
  288. else
  289. emit error(i18n("Invalid DEVNAME: \"%1\"!", devname), true);
  290. }
  291. }
  292. else if (line.startsWith(QStringLiteral("MINTEMP=")))
  293. {
  294. line.remove(QStringLiteral("MINTEMP="));
  295. parseConfigLine(line.simplified(), &PwmFan::setMinTemp);
  296. }
  297. else if (line.startsWith(QStringLiteral("MAXTEMP=")))
  298. {
  299. line.remove(QStringLiteral("MAXTEMP="));
  300. parseConfigLine(line.simplified(), &PwmFan::setMaxTemp);
  301. }
  302. else if (line.startsWith(QStringLiteral("MINSTART=")))
  303. {
  304. line.remove(QStringLiteral("MINSTART="));
  305. parseConfigLine(line.simplified(), &PwmFan::setMinStart);
  306. }
  307. else if (line.startsWith(QStringLiteral("MINSTOP=")))
  308. {
  309. line.remove(QStringLiteral("MINSTOP="));
  310. parseConfigLine(line.simplified(), &PwmFan::setMinStop);
  311. }
  312. else if (line.startsWith(QStringLiteral("MINPWM=")))
  313. {
  314. line.remove(QStringLiteral("MINPWM="));
  315. parseConfigLine(line.simplified(), &PwmFan::setMinPwm);
  316. }
  317. else if (line.startsWith(QStringLiteral("MAXPWM=")))
  318. {
  319. line.remove(QStringLiteral("MAXPWM="));
  320. parseConfigLine(line.simplified(), &PwmFan::setMaxPwm);
  321. }
  322. else if (!line.startsWith(QStringLiteral("DEVPATH=")) &&
  323. !line.startsWith(QStringLiteral("FCFANS=")))
  324. {
  325. //Connect hwmons again
  326. for (const auto &hwmon : m_hwmons)
  327. connect(hwmon, &Hwmon::configUpdateNeeded, this, &Loader::updateConfig);
  328. emit error(i18n("Unrecognized line in config: \"%1\"", line), true);
  329. return false;
  330. }
  331. }
  332. updateConfig();
  333. //Connect hwmons again
  334. for (const auto &hwmon : m_hwmons)
  335. connect(hwmon, &Hwmon::configUpdateNeeded, this, &Loader::updateConfig);
  336. return true;
  337. }
  338. void Loader::parseConfigLine(const QString &line, void (PwmFan::*memberSetFunction)(int))
  339. {
  340. if (!memberSetFunction)
  341. return;
  342. const auto entries = line.split(' ');
  343. for (const auto &entry : entries)
  344. {
  345. const auto fanValuePair = entry.split('=');
  346. if (fanValuePair.size() == 2)
  347. {
  348. const auto pwmFanString = fanValuePair.at(0);
  349. const auto valueString = fanValuePair.at(1);
  350. auto success = false;
  351. const auto value = valueString.toInt(&success);
  352. if (success)
  353. {
  354. auto pwmFanPointer = pwmFan(getEntryNumbers(pwmFanString));
  355. if (pwmFanPointer)
  356. (pwmFanPointer->*memberSetFunction)(value);
  357. else
  358. emit error(i18n("Invalid fan entry: \"%1\"", pwmFanString), true);
  359. }
  360. else
  361. emit error(i18n("%1 is not an integer!", valueString));
  362. }
  363. else
  364. emit error(i18n("Invalid entry to parse: \"%1\"", entry));
  365. }
  366. }
  367. bool Loader::load(const QUrl &url)
  368. {
  369. QString fileName;
  370. if (url.isEmpty())
  371. {
  372. // qDebug() << "Given empty url. Fallback to" << m_configUrl;
  373. fileName = m_configUrl.toLocalFile();
  374. }
  375. else if (url.isValid())
  376. {
  377. if (url.isLocalFile())
  378. fileName = url.toLocalFile();
  379. else
  380. {
  381. emit error(i18n("%1 is not a local file!", url.toDisplayString()));
  382. return false;
  383. }
  384. }
  385. else
  386. {
  387. emit error(i18n("%1 is not a valid url!", url.toDisplayString()));
  388. return false;
  389. }
  390. QTextStream stream;
  391. QFile file(fileName);
  392. QString fileContent;
  393. if (file.open(QFile::ReadOnly | QFile::Text))
  394. {
  395. stream.setDevice(&file);
  396. fileContent = stream.readAll();
  397. }
  398. else if (file.exists())
  399. {
  400. auto action = newFancontrolAction();
  401. if (action.isValid())
  402. {
  403. auto map = QVariantMap();
  404. map[QStringLiteral("action")] = QVariant("read");
  405. map[QStringLiteral("filename")] = fileName;
  406. action.setArguments(map);
  407. auto reply = action.execute();
  408. if (!reply->exec())
  409. {
  410. if (reply->error() == 4)
  411. {
  412. qDebug() << "Loading of file aborted by user";
  413. return false;
  414. }
  415. emit error(reply->errorString() + reply->errorText(), true);
  416. return false;
  417. }
  418. else
  419. fileContent = reply->data().value(QStringLiteral("content")).toString();
  420. }
  421. else
  422. emit error(i18n("Action not supported! Try running the application as root."), true);
  423. }
  424. else
  425. {
  426. emit error(i18n("File does not exist: \"%1\"" ,fileName));
  427. return false;
  428. }
  429. auto success = parseConfig(fileContent);
  430. if (success && !url.isEmpty())
  431. {
  432. m_configUrl = url;
  433. emit configUrlChanged();
  434. }
  435. return success;
  436. }
  437. bool Loader::save(const QUrl &url)
  438. {
  439. QString fileName;
  440. if (url.isEmpty())
  441. {
  442. // qDebug() << "Given empty url. Fallback to " << m_configUrl;
  443. fileName = m_configUrl.toLocalFile();
  444. }
  445. else if (url.isLocalFile())
  446. fileName = url.toLocalFile();
  447. else
  448. {
  449. emit error(i18n("%1 is not a local file!", url.toDisplayString()), true);
  450. return false;
  451. }
  452. QFile file(fileName);
  453. if (file.open(QFile::WriteOnly | QFile::Text))
  454. {
  455. QTextStream stream(&file);
  456. stream << m_configFile;
  457. }
  458. else
  459. {
  460. auto action = newFancontrolAction();
  461. if (action.isValid())
  462. {
  463. QVariantMap map;
  464. map[QStringLiteral("action")] = QVariant("write");
  465. map[QStringLiteral("filename")] = fileName;
  466. map[QStringLiteral("content")] = m_configFile;
  467. action.setArguments(map);
  468. auto reply = action.execute();
  469. if (!reply->exec())
  470. {
  471. if (reply->error() == 4)
  472. {
  473. qDebug() << "Saving of file aborted by user";
  474. return false;
  475. }
  476. emit error(reply->errorString() + reply->errorText(), true);
  477. return false;
  478. }
  479. }
  480. else
  481. emit error(i18n("Action not supported! Try running the application as root."), true);
  482. }
  483. return true;
  484. }
  485. void Loader::updateConfig()
  486. {
  487. const auto configFile = createConfig();
  488. if (configFile != m_configFile)
  489. {
  490. m_configFile = configFile;
  491. emit configFileChanged();
  492. }
  493. }
  494. QString Loader::createConfig() const
  495. {
  496. QList<Hwmon *> usedHwmons;
  497. QList<PwmFan *> usedFans;
  498. for (const auto &hwmon : m_hwmons)
  499. {
  500. if (hwmon->pwmFans().size() > 0 && !usedHwmons.contains(hwmon))
  501. usedHwmons << hwmon;
  502. const auto pwmFans = hwmon->pwmFans();
  503. for (const auto &pwmFan : pwmFans)
  504. {
  505. if (pwmFan->hasTemp() && pwmFan->temp() && !pwmFan->testing())
  506. {
  507. usedFans << pwmFan;
  508. if (!usedHwmons.contains(pwmFan->temp()->parent()))
  509. usedHwmons << pwmFan->temp()->parent();
  510. }
  511. }
  512. }
  513. auto configFile = QStringLiteral("# This file was created by Fancontrol-GUI") + QChar(QChar::LineFeed);
  514. if (m_interval != 0)
  515. configFile += "INTERVAL=" + QString::number(m_interval) + QChar(QChar::LineFeed);
  516. if (!usedHwmons.isEmpty())
  517. {
  518. configFile += "DEVPATH=";
  519. for (const auto &hwmon : usedHwmons)
  520. {
  521. auto sanitizedPath = hwmon->path();
  522. sanitizedPath.remove(QRegExp("^/sys/"));
  523. sanitizedPath.remove(QRegExp("/hwmon/hwmon\\d\\s*$"));
  524. configFile += "hwmon" + QString::number(hwmon->index()) + "=" + sanitizedPath + QChar(QChar::Space);
  525. }
  526. configFile += QChar(QChar::LineFeed);
  527. configFile += "DEVNAME=";
  528. for (const auto &hwmon : usedHwmons)
  529. configFile += "hwmon" + QString::number(hwmon->index()) + "=" + hwmon->name().split('.').first() + QChar(QChar::Space);
  530. configFile += QChar(QChar::LineFeed);
  531. if (!usedFans.isEmpty())
  532. {
  533. configFile += "FCTEMPS=";
  534. for (const auto &pwmFan : usedFans)
  535. {
  536. configFile += "hwmon" + QString::number(pwmFan->parent()->index()) + "/";
  537. configFile += "pwm" + QString::number(pwmFan->index()) + "=";
  538. configFile += "hwmon" + QString::number(pwmFan->temp()->parent()->index()) + "/";
  539. configFile += "temp" + QString::number(pwmFan->temp()->index()) + "_input ";
  540. }
  541. configFile += QChar(QChar::LineFeed);
  542. configFile += "FCFANS=";
  543. for (const auto &pwmFan : usedFans)
  544. {
  545. configFile += "hwmon" + QString::number(pwmFan->parent()->index()) + "/";
  546. configFile += "pwm" + QString::number(pwmFan->index()) + "=";
  547. configFile += "hwmon" + QString::number(pwmFan->parent()->index()) + "/";
  548. configFile += "fan" + QString::number(pwmFan->index()) + "_input ";
  549. }
  550. configFile += QChar(QChar::LineFeed);
  551. configFile += "MINTEMP=";
  552. for (const auto &pwmFan : usedFans)
  553. {
  554. configFile += "hwmon" + QString::number(pwmFan->parent()->index()) + "/";
  555. configFile += "pwm" + QString::number(pwmFan->index()) + "=";
  556. configFile += QString::number(pwmFan->minTemp()) + QChar(QChar::Space);
  557. }
  558. configFile += QChar(QChar::LineFeed);
  559. configFile += "MAXTEMP=";
  560. for (const auto &pwmFan : usedFans)
  561. {
  562. configFile += "hwmon" + QString::number(pwmFan->parent()->index()) + "/";
  563. configFile += "pwm" + QString::number(pwmFan->index()) + "=";
  564. configFile += QString::number(pwmFan->maxTemp()) + QChar(QChar::Space);
  565. }
  566. configFile += QChar(QChar::LineFeed);
  567. configFile += "MINSTART=";
  568. for (const auto &pwmFan : usedFans)
  569. {
  570. configFile += "hwmon" + QString::number(pwmFan->parent()->index()) + "/";
  571. configFile += "pwm" + QString::number(pwmFan->index()) + "=";
  572. configFile += QString::number(pwmFan->minStart()) + QChar(QChar::Space);
  573. }
  574. configFile += QChar(QChar::LineFeed);
  575. configFile += "MINSTOP=";
  576. for (const auto &pwmFan : usedFans)
  577. {
  578. configFile += "hwmon" + QString::number(pwmFan->parent()->index()) + "/";
  579. configFile += "pwm" + QString::number(pwmFan->index()) + "=";
  580. configFile += QString::number(pwmFan->minStop()) + QChar(QChar::Space);
  581. }
  582. configFile += QChar(QChar::LineFeed);
  583. configFile += "MINPWM=";
  584. for (const auto &pwmFan : usedFans)
  585. {
  586. configFile += "hwmon" + QString::number(pwmFan->parent()->index()) + "/";
  587. configFile += "pwm" + QString::number(pwmFan->index()) + "=";
  588. configFile += QString::number(pwmFan->minPwm()) + QChar(QChar::Space);
  589. }
  590. configFile += QChar(QChar::LineFeed);
  591. configFile += "MAXPWM=";
  592. for (const auto &pwmFan : usedFans)
  593. {
  594. configFile += "hwmon" + QString::number(pwmFan->parent()->index()) + "/";
  595. configFile += "pwm" + QString::number(pwmFan->index()) + "=";
  596. configFile += QString::number(pwmFan->maxPwm()) + QChar(QChar::Space);
  597. }
  598. configFile += QChar(QChar::LineFeed);
  599. }
  600. }
  601. return configFile;
  602. }
  603. void Loader::setInterval(int interval, bool writeNewConfig)
  604. {
  605. if (interval < 1)
  606. {
  607. emit error(i18n("Interval must be greater or equal to one!"), true);
  608. return;
  609. }
  610. if (interval != m_interval)
  611. {
  612. m_interval = interval;
  613. emit intervalChanged();
  614. if (writeNewConfig)
  615. updateConfig();
  616. }
  617. }
  618. void Loader::testFans()
  619. {
  620. for (const auto &hwmon : m_hwmons)
  621. hwmon->testFans();
  622. }
  623. void Loader::abortTestingFans()
  624. {
  625. for (const auto &hwmon : m_hwmons)
  626. hwmon->abortTestingFans();
  627. }
  628. void Loader::detectSensors()
  629. {
  630. auto program = QStringLiteral("sensors-detect");
  631. auto arguments = QStringList() << QStringLiteral("--auto");
  632. auto process = new QProcess(this);
  633. process->start(program, arguments);
  634. connect(process, static_cast<void(QProcess::*)(int)>(&QProcess::finished),
  635. this, static_cast<void(Loader::*)(int)>(&Loader::handleDetectSensorsResult));
  636. }
  637. void Loader::handleDetectSensorsResult(int exitCode)
  638. {
  639. auto process = qobject_cast<QProcess *>(sender());
  640. if (exitCode)
  641. {
  642. if (process)
  643. emit error(process->readAllStandardOutput());
  644. auto action = newFancontrolAction();
  645. if (action.isValid())
  646. {
  647. QVariantMap map;
  648. map[QStringLiteral("action")] = QVariant("detectSensors");
  649. action.setArguments(map);
  650. auto job = action.execute();
  651. connect(job, &KAuth::ExecuteJob::result, this, static_cast<void(Loader::*)(KJob *)>(&Loader::handleDetectSensorsResult));
  652. job->start();
  653. }
  654. else
  655. emit error(i18n("Action not supported! Try running the application as root."), true);
  656. }
  657. else
  658. {
  659. if (!m_sensorsDetected)
  660. {
  661. m_sensorsDetected = true;
  662. emit sensorsDetectedChanged();
  663. }
  664. parseHwmons();
  665. }
  666. if (process)
  667. process->deleteLater();
  668. }
  669. void Loader::handleDetectSensorsResult(KJob *job)
  670. {
  671. if (job->error())
  672. {
  673. if (job->error() == 4)
  674. {
  675. // qDebug() << "Aborted by user";
  676. return;
  677. }
  678. emit error(job->errorString() + job->errorText(), true);
  679. }
  680. else
  681. {
  682. if (!m_sensorsDetected)
  683. {
  684. m_sensorsDetected = true;
  685. emit sensorsDetectedChanged();
  686. }
  687. parseHwmons();
  688. }
  689. }
  690. QList<QObject *> Loader::hwmonsAsObjects() const
  691. {
  692. auto list = QList<QObject *>();
  693. for (const auto &hwmon : m_hwmons)
  694. list << qobject_cast<QObject *>(hwmon);
  695. return list;
  696. }
  697. void Loader::handleTestStatusChanged()
  698. {
  699. auto testing = false;
  700. for (const auto &hwmon : m_hwmons)
  701. {
  702. if (hwmon->testing() == true)
  703. {
  704. testing = true;
  705. break;
  706. }
  707. }
  708. if (!testing && !m_reactivateAfterTesting)
  709. return;
  710. emit requestSetServiceActive(!testing);
  711. }
  712. void Loader::setRestartServiceAfterTesting(bool restart)
  713. {
  714. if (m_reactivateAfterTesting == restart)
  715. return;
  716. m_reactivateAfterTesting = restart;
  717. emit restartServiceAfterTestingChanged();
  718. }
  719. void Loader::reset() const
  720. {
  721. for (const auto &hwmon : m_hwmons)
  722. hwmon->reset();
  723. }
  724. }