loader.cpp 25 KB

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