loader.cpp 25 KB

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