diff --git a/libdiscover/backends/PackageKitBackend/PackageKitUpdater.cpp b/libdiscover/backends/PackageKitBackend/PackageKitUpdater.cpp index 2eae1852..d1fe522c 100644 --- a/libdiscover/backends/PackageKitBackend/PackageKitUpdater.cpp +++ b/libdiscover/backends/PackageKitBackend/PackageKitUpdater.cpp @@ -1,450 +1,451 @@ /*************************************************************************** * Copyright © 2013 Lukas Appelhans * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ***************************************************************************/ #include "PackageKitUpdater.h" #include "PackageKitMessages.h" #include #include #include #include #include #include #include #include int percentageWithStatus(PackageKit::Transaction::Status status, uint percentage) { const auto was = percentage; if (status != PackageKit::Transaction::StatusUnknown) { static const QMap statuses = { { PackageKit::Transaction::Status::StatusDownload, 0 }, { PackageKit::Transaction::Status::StatusInstall, 1}, { PackageKit::Transaction::Status::StatusRemove, 1}, { PackageKit::Transaction::Status::StatusLoadingCache, 1}, { PackageKit::Transaction::Status::StatusUpdate, 1} }; const auto idx = statuses.value(status, -1); if (idx < 0) { qDebug() << "Status not present" << status << "among" << statuses .keys() << percentage; return -1; } percentage = (idx * 100 + percentage) / 2 /*the maximum in statuses*/; } qDebug() << "reporting progress with status:" << status << percentage << was; return percentage; } PackageKitUpdater::PackageKitUpdater(PackageKitBackend * parent) : AbstractBackendUpdater(parent), m_transaction(nullptr), m_backend(parent), m_isCancelable(false), m_isProgressing(false), m_percentage(0), m_lastUpdate() { fetchLastUpdateTime(); } PackageKitUpdater::~PackageKitUpdater() { } void PackageKitUpdater::prepare() { if (PackageKit::Daemon::global()->offline()->updateTriggered()) { m_toUpgrade.clear(); m_allUpgradeable.clear(); enableNeedsReboot(); return; } Q_ASSERT(!m_transaction); m_toUpgrade = m_backend->upgradeablePackages(); m_allUpgradeable = m_toUpgrade; } void PackageKitUpdater::setupTransaction(PackageKit::Transaction::TransactionFlags flags) { m_packagesModified.clear(); auto pkgs = involvedPackages(m_toUpgrade).toList(); pkgs.sort(); m_transaction = PackageKit::Daemon::updatePackages(pkgs, flags); m_isCancelable = m_transaction->allowCancel(); + cancellableChanged(); connect(m_transaction.data(), &PackageKit::Transaction::finished, this, &PackageKitUpdater::finished); connect(m_transaction.data(), &PackageKit::Transaction::package, this, &PackageKitUpdater::packageResolved); connect(m_transaction.data(), &PackageKit::Transaction::errorCode, this, &PackageKitUpdater::errorFound); connect(m_transaction.data(), &PackageKit::Transaction::mediaChangeRequired, this, &PackageKitUpdater::mediaChange); connect(m_transaction.data(), &PackageKit::Transaction::requireRestart, this, &PackageKitUpdater::requireRestart); connect(m_transaction.data(), &PackageKit::Transaction::eulaRequired, this, &PackageKitUpdater::eulaRequired); connect(m_transaction.data(), &PackageKit::Transaction::repoSignatureRequired, this, &PackageKitUpdater::repoSignatureRequired); connect(m_transaction.data(), &PackageKit::Transaction::allowCancelChanged, this, &PackageKitUpdater::cancellableChanged); connect(m_transaction.data(), &PackageKit::Transaction::percentageChanged, this, &PackageKitUpdater::percentageChanged); connect(m_transaction.data(), &PackageKit::Transaction::itemProgress, this, &PackageKitUpdater::itemProgress); connect(m_transaction.data(), &PackageKit::Transaction::speedChanged, this, [this] { Q_EMIT downloadSpeedChanged(downloadSpeed()); }); } QSet PackageKitUpdater::packagesForPackageId(const QSet& pkgids) const { QSet packages; packages.reserve(pkgids.size()); foreach(const QString& pkgid, pkgids) { packages += PackageKit::Daemon::packageName(pkgid); } QSet ret; foreach (AbstractResource * res, m_allUpgradeable) { PackageKitResource* pres = qobject_cast(res); if (packages.contains(pres->allPackageNames().toSet())) { ret.insert(res); } } return ret; } QSet PackageKitUpdater::involvedPackages(const QSet& packages) const { QSet packageIds; packageIds.reserve(packages.size()); foreach (AbstractResource * res, packages) { PackageKitResource * app = qobject_cast(res); QString pkgid = m_backend->upgradeablePackageId(app); if (pkgid.isEmpty()) { qWarning() << "no upgradeablePackageId for" << app; continue; } packageIds.insert(pkgid); } return packageIds; } void PackageKitUpdater::processProceedFunction() { auto t = m_proceedFunctions.takeFirst()(); connect(t, &PackageKit::Transaction::finished, this, [this](PackageKit::Transaction::Exit status) { if (status != PackageKit::Transaction::Exit::ExitSuccess) { qWarning() << "transaction failed" << sender() << status; cancel(); return; } if (!m_proceedFunctions.isEmpty()) { processProceedFunction(); } else { start(); } }); } void PackageKitUpdater::proceed() { if (!m_proceedFunctions.isEmpty()) processProceedFunction(); else if (useOfflineUpdates()) setupTransaction(PackageKit::Transaction::TransactionFlagOnlyTrusted | PackageKit::Transaction::TransactionFlagOnlyDownload); else setupTransaction(PackageKit::Transaction::TransactionFlagOnlyTrusted); } bool PackageKitUpdater::useOfflineUpdates() const { if (qEnvironmentVariableIsSet("PK_OFFLINE_UPDATE")) return true; KConfigGroup group(KSharedConfig::openConfig(), "Software"); return group.readEntry("UseOfflineUpdates", false); } void PackageKitUpdater::setUseOfflineUpdates(bool use) { // To enable from command line use: // kwriteconfig5 --file discoverrc --group Software --key UseOfflineUpdates true KConfigGroup group(KSharedConfig::openConfig(), "Software"); group.writeEntry("UseOfflineUpdates", use); } void PackageKitUpdater::start() { Q_ASSERT(!isProgressing()); setupTransaction(PackageKit::Transaction::TransactionFlagSimulate); setProgressing(true); } void PackageKitUpdater::finished(PackageKit::Transaction::Exit exit, uint /*time*/) { // qDebug() << "update finished!" << exit << time; if (!m_proceedFunctions.isEmpty()) return; const bool cancel = exit == PackageKit::Transaction::ExitCancelled; const bool simulate = m_transaction->transactionFlags() & PackageKit::Transaction::TransactionFlagSimulate; disconnect(m_transaction, nullptr, this, nullptr); m_transaction = nullptr; if (!cancel && simulate) { const auto toremove = m_packagesModified.value(PackageKit::Transaction::InfoRemoving); if (!toremove.isEmpty()) { const auto toinstall = QStringList() << m_packagesModified.value(PackageKit::Transaction::InfoInstalling) << m_packagesModified.value(PackageKit::Transaction::InfoUpdating); Q_EMIT proceedRequest(i18n("Packages to remove"), i18n("The following packages will be removed by the update:\n
  • %1
\nin order to install:\n
  • %2
", PackageKitResource::joinPackages(toremove, QStringLiteral("
  • "), {}), PackageKitResource::joinPackages(toinstall, QStringLiteral("
  • "), {}) )); } else { proceed(); } return; } setProgressing(false); m_backend->fetchUpdates(); fetchLastUpdateTime(); if (useOfflineUpdates()) { PackageKit::Daemon::global()->offline()->trigger(PackageKit::Offline::ActionReboot); Q_EMIT passiveMessage(i18n("Please restart the computer to finish the update")); } } void PackageKitUpdater::cancellableChanged() { if (m_isCancelable != m_transaction->allowCancel()) { m_isCancelable = m_transaction->allowCancel(); emit cancelableChanged(m_isCancelable); } } void PackageKitUpdater::percentageChanged() { const auto actualPercentage = percentageWithStatus(m_transaction->status(), m_transaction->percentage()); if (actualPercentage >= 0 && m_percentage != actualPercentage) { m_percentage = actualPercentage; emit progressChanged(m_percentage); } } bool PackageKitUpdater::hasUpdates() const { return m_backend->updatesCount() > 0; } qreal PackageKitUpdater::progress() const { return m_percentage; } void PackageKitUpdater::removeResources(const QList& apps) { QSet pkgs = involvedPackages(apps.toSet()); m_toUpgrade.subtract(packagesForPackageId(pkgs)); } void PackageKitUpdater::addResources(const QList& apps) { QSet pkgs = involvedPackages(apps.toSet()); m_toUpgrade.unite(packagesForPackageId(pkgs)); } QList PackageKitUpdater::toUpdate() const { return m_toUpgrade.toList(); } bool PackageKitUpdater::isMarked(AbstractResource* res) const { return m_toUpgrade.contains(res); } QDateTime PackageKitUpdater::lastUpdate() const { return m_lastUpdate; } bool PackageKitUpdater::isCancelable() const { return m_isCancelable; } bool PackageKitUpdater::isProgressing() const { return m_isProgressing; } void PackageKitUpdater::cancel() { if (m_transaction) m_transaction->cancel(); else setProgressing(false); } void PackageKitUpdater::errorFound(PackageKit::Transaction::Error err, const QString& error) { if (err == PackageKit::Transaction::ErrorNoLicenseAgreement) return; Q_EMIT passiveMessage(QStringLiteral("%1\n%2").arg(PackageKitMessages::errorMessage(err), error)); qWarning() << "Error happened" << err << error; } void PackageKitUpdater::mediaChange(PackageKit::Transaction::MediaType media, const QString& type, const QString& text) { Q_UNUSED(media) Q_EMIT passiveMessage(i18n("Media Change of type '%1' is requested.\n%2", type, text)); } void PackageKitUpdater::requireRestart(PackageKit::Transaction::Restart restart, const QString& pkgid) { Q_EMIT passiveMessage(PackageKitMessages::restartMessage(restart, pkgid)); } void PackageKitUpdater::eulaRequired(const QString& eulaID, const QString& packageID, const QString& vendor, const QString& licenseAgreement) { m_proceedFunctions << [eulaID](){ return PackageKit::Daemon::acceptEula(eulaID); }; Q_EMIT proceedRequest(i18n("Accept EULA"), i18n("The package %1 and its vendor %2 require that you accept their license:\n %3", PackageKit::Daemon::packageName(packageID), vendor, licenseAgreement)); } void PackageKitUpdater::setProgressing(bool progressing) { if (m_isProgressing != progressing) { m_isProgressing = progressing; emit progressingChanged(m_isProgressing); } } void PackageKitUpdater::fetchLastUpdateTime() { QDBusPendingReply transaction = PackageKit::Daemon::global()->getTimeSinceAction(PackageKit::Transaction::RoleGetUpdates); QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher(transaction, this); connect(watcher, &QDBusPendingCallWatcher::finished, this, &PackageKitUpdater::lastUpdateTimeReceived); } void PackageKitUpdater::lastUpdateTimeReceived(QDBusPendingCallWatcher* w) { QDBusPendingReply reply = w->reply(); if (reply.isError()) { qWarning() << "Error when fetching the last update time" << reply.error(); } else { m_lastUpdate = QDateTime::currentDateTime().addSecs(-int(reply.value())); } w->deleteLater(); } AbstractBackendUpdater::State toUpdateState(PackageKit::Transaction::Status t) { switch(t) { case PackageKit::Transaction::StatusUnknown: case PackageKit::Transaction::StatusDownload: return AbstractBackendUpdater::Downloading; case PackageKit::Transaction::StatusDepResolve: case PackageKit::Transaction::StatusSigCheck: case PackageKit::Transaction::StatusTestCommit: case PackageKit::Transaction::StatusInstall: case PackageKit::Transaction::StatusCommit: return AbstractBackendUpdater::Installing; case PackageKit::Transaction::StatusFinished: case PackageKit::Transaction::StatusCancel: return AbstractBackendUpdater::Done; default: qDebug() << "unknown packagekit status" << t; return AbstractBackendUpdater::None; } Q_UNREACHABLE(); } void PackageKitUpdater::itemProgress(const QString& itemID, PackageKit::Transaction::Status status, uint percentage) { const auto res = packagesForPackageId({itemID}); foreach(auto r, res) { Q_EMIT resourceProgressed(r, percentage, toUpdateState(status)); } } void PackageKitUpdater::fetchChangelog() const { QStringList pkgids; foreach(AbstractResource* res, m_allUpgradeable) { pkgids += static_cast(res)->availablePackageId(); } Q_ASSERT(!pkgids.isEmpty()); PackageKit::Transaction* t = PackageKit::Daemon::getUpdatesDetails(pkgids); connect(t, &PackageKit::Transaction::updateDetail, this, &PackageKitUpdater::updateDetail); connect(t, &PackageKit::Transaction::errorCode, this, &PackageKitUpdater::errorFound); } void PackageKitUpdater::updateDetail(const QString& packageID, const QStringList& updates, const QStringList& obsoletes, const QStringList& vendorUrls, const QStringList& bugzillaUrls, const QStringList& cveUrls, PackageKit::Transaction::Restart restart, const QString& updateText, const QString& changelog, PackageKit::Transaction::UpdateState state, const QDateTime& issued, const QDateTime& updated) { auto res = packagesForPackageId({packageID}); foreach(auto r, res) { static_cast(r)->updateDetail(packageID, updates, obsoletes, vendorUrls, bugzillaUrls, cveUrls, restart, updateText, changelog, state, issued, updated); } } void PackageKitUpdater::packageResolved(PackageKit::Transaction::Info info, const QString& packageId) { m_packagesModified[info] << packageId; } void PackageKitUpdater::repoSignatureRequired(const QString& packageID, const QString& repoName, const QString& keyUrl, const QString& keyUserid, const QString& keyId, const QString& keyFingerprint, const QString& keyTimestamp, PackageKit::Transaction::SigType type) { Q_EMIT proceedRequest(i18n("Missing signature for %1 in %2", packageID, repoName), i18n("Do you trust the following key?\n\nUrl: %1\nUser: %2\nKey: %3\nFingerprint: %4\nTimestamp: %4\n", keyUrl, keyUserid, keyFingerprint, keyTimestamp)); m_proceedFunctions << [type, keyId, packageID](){ return PackageKit::Daemon::installSignature(type, keyId, packageID); }; } double PackageKitUpdater::updateSize() const { double ret = 0.; QSet donePkgs; for (AbstractResource * res : m_toUpgrade) { PackageKitResource * app = qobject_cast(res); QString pkgid = m_backend->upgradeablePackageId(app); if (!donePkgs.contains(pkgid)) { donePkgs.insert(pkgid); ret += app->size(); } } return ret; } quint64 PackageKitUpdater::downloadSpeed() const { return m_transaction ? m_transaction->speed() : 0; } diff --git a/libdiscover/resources/ResourcesUpdatesModel.cpp b/libdiscover/resources/ResourcesUpdatesModel.cpp index 77b0ec11..edc34eed 100644 --- a/libdiscover/resources/ResourcesUpdatesModel.cpp +++ b/libdiscover/resources/ResourcesUpdatesModel.cpp @@ -1,294 +1,294 @@ /*************************************************************************** * Copyright © 2012 Aleix Pol Gonzalez * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ***************************************************************************/ #include "ResourcesUpdatesModel.h" #include #include #include "ResourcesModel.h" #include "AbstractBackendUpdater.h" #include "AbstractResource.h" #include "utils.h" #include #include "libdiscover_debug.h" #include #include class UpdateTransaction : public Transaction { Q_OBJECT public: UpdateTransaction(ResourcesUpdatesModel* /*parent*/, const QVector &updaters) : Transaction(nullptr, nullptr, Transaction::InstallRole) , m_allUpdaters(updaters) { bool cancelable = false; foreach(auto updater, m_allUpdaters) { connect(updater, &AbstractBackendUpdater::progressingChanged, this, &UpdateTransaction::slotProgressingChanged); connect(updater, &AbstractBackendUpdater::downloadSpeedChanged, this, &UpdateTransaction::slotDownloadSpeedChanged); connect(updater, &AbstractBackendUpdater::progressChanged, this, &UpdateTransaction::slotUpdateProgress); connect(updater, &AbstractBackendUpdater::proceedRequest, this, &UpdateTransaction::processProceedRequest); - connect(updater, &AbstractBackendUpdater::cancelableChanged, this, [this](bool cancelable){ if (cancelable) setCancellable(true); }); + connect(updater, &AbstractBackendUpdater::cancelableChanged, this, [this](bool){ setCancellable(kContains(m_allUpdaters, [] (AbstractBackendUpdater* updater) { return updater->isCancelable() && updater->isProgressing(); })); }); cancelable |= updater->isCancelable(); } setCancellable(cancelable); } void processProceedRequest(const QString &title, const QString& message) { m_updatersWaitingForFeedback += qobject_cast(sender()); Q_EMIT proceedRequest(title, message); } void cancel() override { QVector toCancel = m_updatersWaitingForFeedback.isEmpty() ? m_allUpdaters : m_updatersWaitingForFeedback; foreach(auto updater, toCancel) { updater->cancel(); } } void proceed() override { m_updatersWaitingForFeedback.takeFirst()->proceed(); } bool isProgressing() const { bool progressing = false; foreach(AbstractBackendUpdater* upd, m_allUpdaters) { progressing |= upd->isProgressing(); } return progressing; } void slotProgressingChanged() { if (status() > SetupStatus && status() < DoneStatus && !isProgressing()) { setStatus(Transaction::DoneStatus); Q_EMIT finished(); deleteLater(); } } void slotUpdateProgress() { qreal total = 0; foreach(AbstractBackendUpdater* updater, m_allUpdaters) { total += updater->progress(); } setProgress(total / m_allUpdaters.count()); } void slotDownloadSpeedChanged() { quint64 total = 0; foreach(AbstractBackendUpdater* updater, m_allUpdaters) { total += updater->downloadSpeed(); } setDownloadSpeed(total); } QVariant icon() const override { return QStringLiteral("update-low"); } QString name() const override { return i18n("Update"); } Q_SIGNALS: void finished(); private: QVector m_updatersWaitingForFeedback; const QVector m_allUpdaters; }; ResourcesUpdatesModel::ResourcesUpdatesModel(QObject* parent) : QStandardItemModel(parent) , m_lastIsProgressing(false) , m_transaction(nullptr) { connect(ResourcesModel::global(), &ResourcesModel::backendsChanged, this, &ResourcesUpdatesModel::init); init(); } void ResourcesUpdatesModel::init() { const QVector backends = ResourcesModel::global()->backends(); m_lastIsProgressing = false; foreach(AbstractResourcesBackend* b, backends) { AbstractBackendUpdater* updater = b->backendUpdater(); if(updater && !m_updaters.contains(updater)) { connect(updater, &AbstractBackendUpdater::statusMessageChanged, this, &ResourcesUpdatesModel::message); connect(updater, &AbstractBackendUpdater::statusDetailChanged, this, &ResourcesUpdatesModel::message); connect(updater, &AbstractBackendUpdater::downloadSpeedChanged, this, &ResourcesUpdatesModel::downloadSpeedChanged); connect(updater, &AbstractBackendUpdater::resourceProgressed, this, &ResourcesUpdatesModel::resourceProgressed); connect(updater, &AbstractBackendUpdater::passiveMessage, this, &ResourcesUpdatesModel::passiveMessage); connect(updater, &AbstractBackendUpdater::needsRebootChanged, this, &ResourcesUpdatesModel::needsRebootChanged); connect(updater, &AbstractBackendUpdater::destroyed, this, &ResourcesUpdatesModel::updaterDestroyed); m_updaters += updater; m_lastIsProgressing |= updater->isProgressing(); } } auto tm = TransactionModel::global(); foreach(auto t, tm->transactions()) { auto updateTransaction = qobject_cast(t); if (updateTransaction) { setTransaction(updateTransaction); } } } void ResourcesUpdatesModel::updaterDestroyed(QObject* obj) { m_updaters.removeAll(static_cast(obj)); } void ResourcesUpdatesModel::message(const QString& msg) { if(msg.isEmpty()) return; appendRow(new QStandardItem(msg)); } void ResourcesUpdatesModel::prepare() { if(isProgressing()) { qCWarning(LIBDISCOVER_LOG) << "trying to set up a running instance"; return; } foreach(AbstractBackendUpdater* upd, m_updaters) { upd->prepare(); } } void ResourcesUpdatesModel::updateAll() { if (!m_updaters.isEmpty()) { delete m_transaction; const auto updaters = kFilter>(m_updaters, [](AbstractBackendUpdater* u) {return u->hasUpdates(); }); if (updaters.isEmpty()) { return; } m_transaction = new UpdateTransaction(this, updaters); m_transaction->setStatus(Transaction::SetupStatus); setTransaction(m_transaction); TransactionModel::global()->addTransaction(m_transaction); Q_FOREACH (AbstractBackendUpdater* upd, updaters) { QMetaObject::invokeMethod(upd, "start", Qt::QueuedConnection); } QMetaObject::invokeMethod(this, [this](){ m_transaction->setStatus(Transaction::CommittingStatus); m_transaction->slotProgressingChanged(); }, Qt::QueuedConnection); } } bool ResourcesUpdatesModel::isProgressing() const { return m_transaction && m_transaction->status() < Transaction::DoneStatus; } QList ResourcesUpdatesModel::toUpdate() const { QList ret; foreach(AbstractBackendUpdater* upd, m_updaters) { ret += upd->toUpdate(); } return ret; } void ResourcesUpdatesModel::addResources(const QList& resources) { QHash > sortedResources; foreach(AbstractResource* res, resources) { sortedResources[res->backend()] += res; } for(auto it=sortedResources.constBegin(), itEnd=sortedResources.constEnd(); it!=itEnd; ++it) { it.key()->backendUpdater()->addResources(*it); } } void ResourcesUpdatesModel::removeResources(const QList< AbstractResource* >& resources) { QHash > sortedResources; foreach(AbstractResource* res, resources) { sortedResources[res->backend()] += res; } for(auto it=sortedResources.constBegin(), itEnd=sortedResources.constEnd(); it!=itEnd; ++it) { it.key()->backendUpdater()->removeResources(*it); } } QDateTime ResourcesUpdatesModel::lastUpdate() const { QDateTime ret; foreach(AbstractBackendUpdater* upd, m_updaters) { QDateTime current = upd->lastUpdate(); if(!ret.isValid() || (current.isValid() && current>ret)) { ret = current; } } return ret; } double ResourcesUpdatesModel::updateSize() const { double ret = 0.; for(AbstractBackendUpdater* upd: m_updaters) { ret += upd->updateSize(); } return ret; } qint64 ResourcesUpdatesModel::secsToLastUpdate() const { return lastUpdate().secsTo(QDateTime::currentDateTime()); } void ResourcesUpdatesModel::setTransaction(UpdateTransaction* transaction) { m_transaction = transaction; connect(transaction, &UpdateTransaction::finished, this, &ResourcesUpdatesModel::finished); connect(transaction, &UpdateTransaction::finished, this, &ResourcesUpdatesModel::progressingChanged); Q_EMIT progressingChanged(); } Transaction* ResourcesUpdatesModel::transaction() const { return m_transaction.data(); } bool ResourcesUpdatesModel::needsReboot() const { for(auto upd: m_updaters) { if (upd->needsReboot()) return true; } return false; } #include "ResourcesUpdatesModel.moc"