File indexing completed on 2024-04-21 16:29:33
0001 /*************************************************************************** 0002 * Copyright (C) 2012 by Daniel Nicoletti <dantti12@gmail.com> * 0003 * * 0004 * This program is free software; you can redistribute it and/or modify * 0005 * it under the terms of the GNU General Public License as published by * 0006 * the Free Software Foundation; either version 2 of the License, or * 0007 * (at your option) any later version. * 0008 * * 0009 * This program is distributed in the hope that it will be useful, * 0010 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0012 * GNU General Public License for more details. * 0013 * * 0014 * You should have received a copy of the GNU General Public License * 0015 * along with this program; see the file COPYING. If not, write to * 0016 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * 0017 * Boston, MA 02110-1301, USA. * 0018 ***************************************************************************/ 0019 0020 #include "ApperdThread.h" 0021 0022 #include "RefreshCacheTask.h" 0023 #include "Updater.h" 0024 #include "DistroUpgrade.h" 0025 #include "TransactionWatcher.h" 0026 #include "DBusInterface.h" 0027 #include "RebootListener.h" 0028 0029 #include <Enum.h> 0030 #include <Daemon> 0031 0032 #include <QStandardPaths> 0033 #include <KConfig> 0034 #include <KConfigGroup> 0035 #include <KDirWatch> 0036 #include <KProtocolManager> 0037 #include <KLocalizedString> 0038 //#include <Solid/PowerManagement> 0039 #include <KFormat> 0040 0041 #include <QtDBus/QDBusConnection> 0042 #include <QtDBus/QDBusReply> 0043 #include <QDBusServiceWatcher> 0044 0045 #include <limits.h> 0046 0047 #include <QLoggingCategory> 0048 0049 Q_DECLARE_LOGGING_CATEGORY(APPER_DAEMON) 0050 0051 #define FIVE_MIN 360000 0052 #define ONE_MIN 72000 0053 0054 /* 0055 * What we need: 0056 * - Refresh the package cache periodicaly (implies listenning to PK's updates changed) 0057 * - Update or Display Package Updates 0058 * - Display Distro Upgrades 0059 * - Start Sentinel to keep an eye on running transactions 0060 */ 0061 0062 using namespace PackageKit; 0063 //using namespace Solid; 0064 0065 ApperdThread::ApperdThread(QObject *parent) : 0066 QObject(parent), 0067 m_proxyChanged(true), 0068 m_AptRebootListener(new AptRebootListener(this)) 0069 { 0070 } 0071 0072 ApperdThread::~ApperdThread() 0073 { 0074 } 0075 0076 void ApperdThread::init() 0077 { 0078 // connect(PowerManagement::notifier(), SIGNAL(appShouldConserveResourcesChanged(bool)), 0079 // this, SLOT(appShouldConserveResourcesChanged())); 0080 0081 // This timer keeps polling to see if it has 0082 // to refresh the cache 0083 m_qtimer = new QTimer(this); 0084 m_qtimer->setInterval(FIVE_MIN); 0085 connect(m_qtimer, &QTimer::timeout, this, &ApperdThread::poll); 0086 m_qtimer->start(); 0087 0088 //check if any changes to the file occour 0089 //this also prevents from reading when a checkUpdate happens 0090 auto confWatch = new KDirWatch(this); 0091 confWatch->addFile(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + QLatin1String("/apper")); 0092 connect(confWatch, SIGNAL(dirty(QString)), this, SLOT(configFileChanged())); 0093 connect(confWatch, SIGNAL(created(QString)), this, SLOT(configFileChanged())); 0094 connect(confWatch, SIGNAL(deleted(QString)), this, SLOT(configFileChanged())); 0095 confWatch->startScan(); 0096 0097 // Watch for changes in the KDE proxy settings 0098 auto proxyWatch = new KDirWatch(this); 0099 proxyWatch->addFile(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + QLatin1String("/kioslaverc")); 0100 connect(proxyWatch, SIGNAL(dirty(QString)), this, SLOT(proxyChanged())); 0101 connect(proxyWatch, SIGNAL(created(QString)), this, SLOT(proxyChanged())); 0102 connect(proxyWatch, SIGNAL(deleted(QString)), this, SLOT(proxyChanged())); 0103 proxyWatch->startScan(); 0104 0105 Daemon::global()->setHints(QLatin1String("locale=") + QLocale::system().name() + QLatin1String(".UTF-8")); 0106 0107 connect(Daemon::global(), &Daemon::updatesChanged, this, &ApperdThread::updatesChanged); 0108 0109 m_interface = new DBusInterface(this); 0110 0111 m_refreshCache = new RefreshCacheTask(this); 0112 connect(m_interface, &DBusInterface::refreshCache, m_refreshCache, &RefreshCacheTask::refreshCache); 0113 0114 m_updater = new Updater(this); 0115 0116 m_distroUpgrade = new DistroUpgrade(this); 0117 0118 // read the current settings 0119 configFileChanged(); 0120 0121 // In case PackageKit is not running watch for it's registration to configure proxy 0122 auto watcher = new QDBusServiceWatcher(QLatin1String("org.freedesktop.PackageKit"), 0123 QDBusConnection::systemBus(), 0124 QDBusServiceWatcher::WatchForRegistration, 0125 this); 0126 connect(watcher, &QDBusServiceWatcher::serviceRegistered, this, &ApperdThread::setProxy); 0127 0128 // if PackageKit is running check to see if there are running transactons already 0129 bool packagekitIsRunning = nameHasOwner(QLatin1String("org.freedesktop.PackageKit"), 0130 QDBusConnection::systemBus()); 0131 0132 m_transactionWatcher = new TransactionWatcher(packagekitIsRunning, this); 0133 0134 // connect the watch transaction coming from the updater icon to our watcher 0135 connect(m_interface, &DBusInterface::watchTransaction, m_transactionWatcher, &TransactionWatcher::watchTransactionInteractive); 0136 0137 // listen to Debian/Apt reboot signals from other sources (apt) 0138 connect(m_AptRebootListener, &AptRebootListener::requestReboot, m_transactionWatcher, &TransactionWatcher::showRebootNotificationApt); 0139 QTimer::singleShot(2 /*minutes*/ * 60 /*seconds*/ * 1000 /*msec*/, m_AptRebootListener, SLOT(checkForReboot())); 0140 0141 if (packagekitIsRunning) { 0142 // PackageKit is running set the session Proxy 0143 setProxy(); 0144 0145 // If packagekit is already running go check 0146 // for updates 0147 updatesChanged(); 0148 } else { 0149 // Initial check for updates 0150 QTimer::singleShot(ONE_MIN, this, SLOT(updatesChanged())); 0151 } 0152 } 0153 0154 // This is called every 5 minutes 0155 void ApperdThread::poll() 0156 { 0157 if (m_lastRefreshCache.isNull()) { 0158 // This value wasn't set 0159 // convert this to QDateTime 0160 m_lastRefreshCache = getTimeSinceRefreshCache(); 0161 } 0162 0163 // If check for updates is active 0164 if (m_configs[QLatin1String(CFG_INTERVAL)].value<uint>() != Enum::Never) { 0165 // Find out how many seconds passed since last refresh cache 0166 qint64 msecsSinceLastRefresh = (QDateTime::currentDateTime().toMSecsSinceEpoch() - m_lastRefreshCache.toMSecsSinceEpoch()) / 1000; 0167 0168 // If lastRefreshCache is null it means that the cache was never refreshed 0169 if (m_lastRefreshCache.isNull() || msecsSinceLastRefresh > m_configs[QLatin1String(CFG_INTERVAL)].value<uint>()) { 0170 bool ignoreBattery = m_configs[QLatin1String(CFG_CHECK_UP_BATTERY)].value<bool>(); 0171 bool ignoreMobile = m_configs[QLatin1String(CFG_CHECK_UP_MOBILE)].value<bool>(); 0172 if (isSystemReady(ignoreBattery, ignoreMobile)) { 0173 m_refreshCache->refreshCache(); 0174 } 0175 0176 // Invalidate the last time the cache was refreshed 0177 m_lastRefreshCache = QDateTime(); 0178 } 0179 } 0180 } 0181 0182 void ApperdThread::configFileChanged() 0183 { 0184 KConfig config(QLatin1String("apper")); 0185 KConfigGroup checkUpdateGroup(&config, "CheckUpdate"); 0186 m_configs[QLatin1String(CFG_CHECK_UP_BATTERY)] = checkUpdateGroup.readEntry(CFG_CHECK_UP_BATTERY, DEFAULT_CHECK_UP_BATTERY); 0187 m_configs[QLatin1String(CFG_CHECK_UP_MOBILE)] = checkUpdateGroup.readEntry(CFG_CHECK_UP_MOBILE, DEFAULT_CHECK_UP_MOBILE); 0188 m_configs[QLatin1String(CFG_INSTALL_UP_BATTERY)] = checkUpdateGroup.readEntry(CFG_INSTALL_UP_BATTERY, DEFAULT_INSTALL_UP_BATTERY); 0189 m_configs[QLatin1String(CFG_INSTALL_UP_MOBILE)] = checkUpdateGroup.readEntry(CFG_INSTALL_UP_MOBILE, DEFAULT_INSTALL_UP_MOBILE); 0190 m_configs[QLatin1String(CFG_AUTO_UP)] = checkUpdateGroup.readEntry(CFG_AUTO_UP, Enum::AutoUpdateDefault); 0191 m_configs[QLatin1String(CFG_INTERVAL)] = checkUpdateGroup.readEntry(CFG_INTERVAL, Enum::TimeIntervalDefault); 0192 m_configs[QLatin1String(CFG_DISTRO_UPGRADE)] = checkUpdateGroup.readEntry(CFG_DISTRO_UPGRADE, Enum::DistroUpgradeDefault); 0193 m_updater->setConfig(m_configs); 0194 m_distroUpgrade->setConfig(m_configs); 0195 0196 KDirWatch *confWatch = qobject_cast<KDirWatch*>(sender()); 0197 if (confWatch) { 0198 // Check for updates again since the config changed 0199 updatesChanged(); 0200 } 0201 } 0202 0203 void ApperdThread::proxyChanged() 0204 { 0205 // We must reparse the configuration since the values are all cached 0206 KProtocolManager::reparseConfiguration(); 0207 0208 QHash<QString, QString> proxyConfig; 0209 if (KProtocolManager::proxyType() == KProtocolManager::ManualProxy) { 0210 proxyConfig[QLatin1String("http")] = KProtocolManager::proxyFor(QLatin1String("http")); 0211 proxyConfig[QLatin1String("https")] = KProtocolManager::proxyFor(QLatin1String("https")); 0212 proxyConfig[QLatin1String("ftp")] = KProtocolManager::proxyFor(QLatin1String("ftp")); 0213 proxyConfig[QLatin1String("socks")] = KProtocolManager::proxyFor(QLatin1String("socks")); 0214 } 0215 0216 // Check if the proxy settings really changed to avoid setting them twice 0217 if (proxyConfig != m_proxyConfig) { 0218 m_proxyConfig = proxyConfig; 0219 m_proxyChanged = true; 0220 setProxy(); 0221 } 0222 } 0223 0224 void ApperdThread::setProxy() 0225 { 0226 if (!m_proxyChanged) { 0227 return; 0228 } 0229 0230 // If we were called by the watcher it is because PackageKit is running 0231 bool packagekitIsRunning = true; 0232 auto watcher = qobject_cast<QDBusServiceWatcher*>(sender()); 0233 if (!watcher) { 0234 packagekitIsRunning = nameHasOwner(QLatin1String("org.freedesktop.PackageKit"), 0235 QDBusConnection::systemBus()); 0236 } 0237 0238 if (packagekitIsRunning) { 0239 // Apply the proxy changes only if packagekit is running 0240 // use value() to not insert items on the hash 0241 Daemon::global()->setProxy(m_proxyConfig.value(QLatin1String("http")), 0242 m_proxyConfig.value(QLatin1String("https")), 0243 m_proxyConfig.value(QLatin1String("ftp")), 0244 m_proxyConfig.value(QLatin1String("socks")), 0245 QString(), 0246 QString()); 0247 m_proxyChanged = false; 0248 } 0249 } 0250 0251 void ApperdThread::updatesChanged() 0252 { 0253 // update the last time the cache was refreshed 0254 QDateTime lastCacheRefresh; 0255 lastCacheRefresh = getTimeSinceRefreshCache(); 0256 if (lastCacheRefresh != m_lastRefreshCache) { 0257 m_lastRefreshCache = lastCacheRefresh; 0258 } 0259 0260 bool ignoreBattery = m_configs[QLatin1String(CFG_INSTALL_UP_BATTERY)].value<bool>(); 0261 bool ignoreMobile = m_configs[QLatin1String(CFG_INSTALL_UP_MOBILE)].value<bool>(); 0262 0263 // Make sure the user sees the updates 0264 m_updater->checkForUpdates(isSystemReady(ignoreBattery, ignoreMobile)); 0265 m_distroUpgrade->checkDistroUpgrades(); 0266 } 0267 0268 void ApperdThread::appShouldConserveResourcesChanged() 0269 { 0270 bool ignoreBattery = m_configs[QLatin1String(CFG_INSTALL_UP_BATTERY)].value<bool>(); 0271 bool ignoreMobile = m_configs[QLatin1String(CFG_INSTALL_UP_MOBILE)].value<bool>(); 0272 0273 if (isSystemReady(ignoreBattery, ignoreMobile)) { 0274 m_updater->setSystemReady(); 0275 } 0276 } 0277 0278 QDateTime ApperdThread::getTimeSinceRefreshCache() const 0279 { 0280 uint value = Daemon::global()->getTimeSinceAction(Transaction::RoleRefreshCache); 0281 0282 // When the refresh cache value was not yet defined UINT_MAX is returned 0283 if (value == UINT_MAX) { 0284 return QDateTime(); 0285 } else { 0286 // Calculate the last time the cache was refreshed by 0287 // subtracting the seconds from the current time 0288 return QDateTime::currentDateTime().addSecs(value * -1); 0289 } 0290 } 0291 0292 bool ApperdThread::nameHasOwner(const QString &name, const QDBusConnection &connection) 0293 { 0294 QDBusMessage message; 0295 message = QDBusMessage::createMethodCall(QLatin1String("org.freedesktop.DBus"), 0296 QLatin1String("/"), 0297 QLatin1String("org.freedesktop.DBus"), 0298 QLatin1String("NameHasOwner")); 0299 message << qVariantFromValue(name); 0300 QDBusReply<bool> reply = connection.call(message); 0301 return reply.value(); 0302 } 0303 0304 bool ApperdThread::isSystemReady(bool ignoreBattery, bool ignoreMobile) const 0305 { 0306 // First check if we should conserve resources 0307 // check how applications should behave (e.g. on battery power) 0308 // if (!ignoreBattery && Solid::PowerManagement::appShouldConserveResources()) { 0309 qCDebug(APPER_DAEMON) << "System is not ready, application should conserve resources"; 0310 // This was fixed for KDElibs 4.8.5 0311 return false; 0312 // } 0313 0314 // TODO it would be nice is Solid provided this 0315 // so we wouldn't be waking up PackageKit for this Solid task. 0316 Daemon::Network network = Daemon::global()->networkState(); 0317 // test whether network is connected 0318 if (network == Daemon::NetworkOffline || network == Daemon::NetworkUnknown) { 0319 qCDebug(APPER_DAEMON) << "System is not ready, network state" << network; 0320 return false; 0321 } 0322 0323 // check how applications should behave (e.g. on battery power) 0324 if (!ignoreMobile && network == Daemon::NetworkMobile) { 0325 qCDebug(APPER_DAEMON) << "System is not ready, network state" << network; 0326 return false; 0327 } 0328 0329 return true; 0330 } 0331 0332 #include "moc_ApperdThread.cpp"