File indexing completed on 2024-04-28 05:26:47

0001 /*
0002  *   SPDX-FileCopyrightText: 2020 Aleix Pol Gonzalez <aleixpol@kde.org>
0003  *
0004  *   SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005  */
0006 
0007 #include "UnattendedUpdates.h"
0008 #include "DiscoverNotifier.h"
0009 #include "updatessettings.h"
0010 #include <KIdleTime>
0011 #include <QDateTime>
0012 #include <QDebug>
0013 #include <QProcess>
0014 #include <chrono>
0015 
0016 UnattendedUpdates::UnattendedUpdates(DiscoverNotifier *parent)
0017     : QObject(parent)
0018 {
0019     connect(parent, &DiscoverNotifier::stateChanged, this, &UnattendedUpdates::checkNewState);
0020     connect(KIdleTime::instance(), QOverload<int, int>::of(&KIdleTime::timeoutReached), this, &UnattendedUpdates::triggerUpdate);
0021 
0022     checkNewState();
0023 }
0024 
0025 UnattendedUpdates::~UnattendedUpdates() noexcept
0026 {
0027     if (m_idleTimeoutId.has_value()) {
0028         KIdleTime::instance()->removeIdleTimeout(m_idleTimeoutId.value());
0029     }
0030 }
0031 
0032 void UnattendedUpdates::checkNewState()
0033 {
0034     using namespace std::chrono_literals;
0035     DiscoverNotifier *notifier = static_cast<DiscoverNotifier *>(parent());
0036 
0037     // Only allow offline updating every 3h. It should keep some peace to our users, especially on rolling distros
0038     const QDateTime updateableTime = notifier->settings()->lastUnattendedTrigger().addSecs(std::chrono::seconds(3h).count());
0039     if (updateableTime > QDateTime::currentDateTimeUtc()) {
0040         qDebug() << "skipping update, already updated on" << notifier->settings()->lastUnattendedTrigger().toString();
0041         return;
0042     }
0043 
0044     const bool doTrigger = notifier->hasUpdates() && !notifier->isBusy();
0045     if (doTrigger && !m_idleTimeoutId.has_value()) {
0046         qDebug() << "waiting for an idle moment";
0047         // If the system is untouched for 15 minutes, trigger the unattened update
0048         m_idleTimeoutId = KIdleTime::instance()->addIdleTimeout(int(std::chrono::milliseconds(15min).count()));
0049     } else if (!doTrigger && m_idleTimeoutId.has_value()) {
0050         qDebug() << "nothing to do";
0051         KIdleTime::instance()->removeIdleTimeout(m_idleTimeoutId.value());
0052         m_idleTimeoutId.reset();
0053     }
0054 }
0055 
0056 void UnattendedUpdates::triggerUpdate(int timeoutId)
0057 {
0058     if (!m_idleTimeoutId.has_value() || timeoutId != m_idleTimeoutId.value()) {
0059         return;
0060     }
0061 
0062     KIdleTime::instance()->removeIdleTimeout(m_idleTimeoutId.value());
0063     m_idleTimeoutId.reset();
0064 
0065     DiscoverNotifier *notifier = static_cast<DiscoverNotifier *>(parent());
0066     if (!notifier->hasUpdates() || notifier->isBusy()) {
0067         return;
0068     }
0069 
0070     auto process = new QProcess(this);
0071     connect(process, &QProcess::errorOccurred, this, [](QProcess::ProcessError error) {
0072         qWarning() << "Error running plasma-discover-update" << error;
0073     });
0074     connect(process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, [this, process](int exitCode, QProcess::ExitStatus exitStatus) {
0075         qDebug() << "Finished running plasma-discover-update" << exitCode << exitStatus;
0076         DiscoverNotifier *notifier = static_cast<DiscoverNotifier *>(parent());
0077         process->deleteLater();
0078         notifier->settings()->setLastUnattendedTrigger(QDateTime::currentDateTimeUtc());
0079         notifier->settings()->save();
0080         notifier->setBusy(false);
0081     });
0082 
0083     notifier->setBusy(true);
0084     process->start(QStringLiteral("plasma-discover-update"), {QStringLiteral("--offline")});
0085     qInfo() << "started unattended update" << QDateTime::currentDateTimeUtc();
0086 }