File indexing completed on 2024-04-21 16:29:34
0001 /*************************************************************************** 0002 * Copyright (C) 2008-2011 by Daniel Nicoletti * 0003 * dantti12@gmail.com * 0004 * * 0005 * This program is free software; you can redistribute it and/or modify * 0006 * it under the terms of the GNU General Public License as published by * 0007 * the Free Software Foundation; either version 2 of the License, or * 0008 * (at your option) any later version. * 0009 * * 0010 * This program is distributed in the hope that it will be useful, * 0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0013 * GNU General Public License for more details. * 0014 * * 0015 * You should have received a copy of the GNU General Public License * 0016 * along with this program; see the file COPYING. If not, write to * 0017 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * 0018 * Boston, MA 02110-1301, USA. * 0019 ***************************************************************************/ 0020 0021 #include "TransactionWatcher.h" 0022 0023 #include "TransactionJob.h" 0024 0025 #include <PkStrings.h> 0026 #include <PkIcons.h> 0027 #include <PackageImportance.h> 0028 0029 #include <KNotification> 0030 #include <KLocalizedString> 0031 #include <KMessageBox> 0032 #include <KNotification> 0033 //#include <KComponentData> 0034 0035 //#include <Solid/PowerManagement> 0036 #include <QtDBus/QDBusMessage> 0037 #include <QtDBus/QDBusConnection> 0038 0039 #include <kworkspace5/kworkspace.h> 0040 #include <Daemon> 0041 0042 #include <QLoggingCategory> 0043 0044 Q_DECLARE_LOGGING_CATEGORY(APPER_DAEMON) 0045 0046 TransactionWatcher::TransactionWatcher(bool packagekitIsRunning, QObject *parent) : 0047 QObject(parent), 0048 m_inhibitCookie(-1) 0049 { 0050 m_tracker = new KUiServerJobTracker(this); 0051 0052 // keep track of new transactions 0053 connect(Daemon::global(), &Daemon::transactionListChanged, this, &TransactionWatcher::transactionListChanged); 0054 0055 // if PackageKit is running check to see if there are running transactons already 0056 if (packagekitIsRunning) { 0057 // here we check whether a transaction job should be created or not 0058 QStringList tids; 0059 const QList<QDBusObjectPath> paths = Daemon::global()->getTransactionList(); 0060 for (const QDBusObjectPath &path : paths) { 0061 tids << path.path(); 0062 } 0063 transactionListChanged(tids); 0064 } 0065 } 0066 0067 TransactionWatcher::~TransactionWatcher() 0068 { 0069 // release any cookie that we might have 0070 suppressSleep(false, m_inhibitCookie); 0071 } 0072 0073 void TransactionWatcher::watchTransactionInteractive(const QDBusObjectPath &tid) 0074 { 0075 watchTransaction(tid); 0076 } 0077 0078 void TransactionWatcher::transactionListChanged(const QStringList &tids) 0079 { 0080 if (tids.isEmpty()) { 0081 // release any cookie that we might have 0082 suppressSleep(false, m_inhibitCookie); 0083 } else { 0084 for (const QString &tid : tids) { 0085 watchTransaction(QDBusObjectPath(tid), false); 0086 } 0087 } 0088 } 0089 0090 void TransactionWatcher::watchTransaction(const QDBusObjectPath &tid, bool interactive) 0091 { 0092 Transaction *transaction; 0093 if (!m_transactions.contains(tid)) { 0094 // Check if the current transaction is still the same 0095 transaction = new Transaction(tid); 0096 connect(transaction, &Transaction::roleChanged, this, &TransactionWatcher::transactionReady); 0097 connect(transaction, &Transaction::finished, this, &TransactionWatcher::finished); 0098 0099 // Store the transaction id 0100 m_transactions[tid] = transaction; 0101 } else { 0102 transaction = m_transactions[tid]; 0103 0104 if (transaction->role() != Transaction::RoleUnknown) { 0105 // force the first changed or create a TransactionJob 0106 transactionChanged(transaction, interactive); 0107 } 0108 } 0109 } 0110 0111 void TransactionWatcher::transactionReady() 0112 { 0113 auto transaction = qobject_cast<Transaction*>(sender()); 0114 0115 Transaction::Role role = transaction->role(); 0116 Transaction::TransactionFlags flags = transaction->transactionFlags(); 0117 if (!(flags & Transaction::TransactionFlagOnlyDownload || flags & Transaction::TransactionFlagSimulate) && 0118 (role == Transaction::RoleInstallPackages || 0119 role == Transaction::RoleInstallFiles || 0120 role == Transaction::RoleRemovePackages || 0121 role == Transaction::RoleUpdatePackages)) { 0122 // AVOID showing messages and restart requires when 0123 // the user was just simulating an instalation 0124 connect(transaction, &Transaction::requireRestart, this, &TransactionWatcher::requireRestart); 0125 0126 // Don't let the system sleep while doing some sensible actions 0127 suppressSleep(true, m_inhibitCookie, PkStrings::action(role, flags)); 0128 } 0129 0130 connect(transaction, &Transaction::isCallerActiveChanged, this, [this, transaction] () { 0131 transactionChanged(transaction); 0132 }); 0133 0134 } 0135 0136 void TransactionWatcher::showRebootNotificationApt() { 0137 // Create the notification about this transaction 0138 auto notify = new KNotification(QLatin1String("RestartRequired"), nullptr, KNotification::Persistent); 0139 connect(notify, QOverload<uint>::of(&KNotification::activated), this, &TransactionWatcher::logout); 0140 notify->setComponentName(QLatin1String("apperd")); 0141 0142 QString text(QLatin1String("<b>") + i18n("The system update has completed") + QLatin1String("</b>")); 0143 text.append(QLatin1String("<br>") + PkStrings::restartType(Transaction::RestartSystem)); 0144 notify->setPixmap(PkIcons::restartIcon(Transaction::RestartSystem).pixmap(KPK_ICON_SIZE, KPK_ICON_SIZE)); 0145 notify->setText(text); 0146 0147 // TODO RestartApplication should be handled differently 0148 QStringList actions; 0149 actions << i18n("Restart"); 0150 notify->setActions(actions); 0151 0152 notify->sendEvent(); 0153 } 0154 0155 void TransactionWatcher::finished(PackageKit::Transaction::Exit exit) 0156 { 0157 // check if the transaction emitted any require restart 0158 auto transaction = qobject_cast<Transaction*>(sender()); 0159 QDBusObjectPath tid = transaction->tid(); 0160 transaction->disconnect(this); 0161 m_transactions.remove(tid); 0162 m_transactionJob.remove(tid); 0163 0164 if (exit == Transaction::ExitSuccess && !transaction->property("restartType").isNull()) { 0165 Transaction::Restart type = transaction->property("restartType").value<Transaction::Restart>(); 0166 QStringList restartPackages = transaction->property("restartPackages").toStringList(); 0167 0168 // Create the notification about this transaction 0169 auto notify = new KNotification(QLatin1String("RestartRequired"), nullptr, KNotification::Persistent); 0170 connect(notify, QOverload<uint>::of(&KNotification::activated), this, &TransactionWatcher::logout); 0171 notify->setComponentName(QLatin1String("apperd")); 0172 notify->setProperty("restartType", qVariantFromValue(type)); 0173 notify->setPixmap(PkIcons::restartIcon(type).pixmap(KPK_ICON_SIZE, KPK_ICON_SIZE)); 0174 notify->setTitle(PkStrings::restartType(type)); 0175 0176 // Create a readable text with package names that required the restart 0177 if (!restartPackages.isEmpty()) { 0178 restartPackages.removeDuplicates(); 0179 restartPackages.sort(); 0180 0181 QString text; 0182 text = i18np("Package: %2", 0183 "Packages: %2", 0184 restartPackages.size(), 0185 restartPackages.join(QLatin1String(", "))); 0186 notify->setText(text); 0187 } 0188 0189 // TODO RestartApplication should be handled differently 0190 QStringList actions; 0191 actions << i18n("Restart"); 0192 notify->setActions(actions); 0193 0194 notify->sendEvent(); 0195 } 0196 } 0197 0198 void TransactionWatcher::transactionChanged(Transaction *transaction, bool interactive) 0199 { 0200 if (!transaction) { 0201 transaction = qobject_cast<Transaction*>(sender()); 0202 } 0203 0204 QDBusObjectPath tid = transaction->tid(); 0205 if (!interactive) { 0206 interactive = !transaction->isCallerActive(); 0207 } 0208 0209 // If the 0210 if (!m_transactionJob.contains(tid) && interactive) { 0211 auto job = new TransactionJob(transaction, this); 0212 connect(transaction, &Transaction::errorCode, this, &TransactionWatcher::errorCode); 0213 connect(job, &TransactionJob::canceled, this, &TransactionWatcher::watchedCanceled); 0214 m_tracker->registerJob(job); 0215 m_transactionJob[tid] = job; 0216 job->start(); 0217 } 0218 } 0219 0220 void TransactionWatcher::errorCode(PackageKit::Transaction::Error err, const QString &details) 0221 { 0222 auto notify = new KNotification(QLatin1String("TransactionError"), nullptr, KNotification::Persistent); 0223 notify->setComponentName(QLatin1String("apperd")); 0224 notify->setTitle(PkStrings::error(err)); 0225 notify->setText(PkStrings::errorMessage(err)); 0226 notify->setProperty("ErrorType", QVariant::fromValue(err)); 0227 notify->setProperty("Details", details); 0228 0229 QStringList actions; 0230 actions << i18n("Details"); 0231 notify->setActions(actions); 0232 notify->setPixmap(QIcon::fromTheme(QLatin1String("dialog-error")).pixmap(KPK_ICON_SIZE, KPK_ICON_SIZE)); 0233 connect(notify, QOverload<uint>::of(&KNotification::activated), this, &TransactionWatcher::errorActivated); 0234 notify->sendEvent(); 0235 } 0236 0237 void TransactionWatcher::errorActivated(uint action) 0238 { 0239 auto notify = qobject_cast<KNotification*>(sender()); 0240 0241 // if the user clicked "Details" 0242 if (action == 1) { 0243 Transaction::Error error = notify->property("ErrorType").value<Transaction::Error>(); 0244 QString details = notify->property("Details").toString(); 0245 KMessageBox::detailedError(nullptr, 0246 PkStrings::errorMessage(error), 0247 details.replace(QLatin1Char('\n'), QLatin1String("<br>")), 0248 PkStrings::error(error), 0249 KMessageBox::Notify); 0250 } 0251 0252 notify->close(); 0253 } 0254 0255 void TransactionWatcher::requireRestart(PackageKit::Transaction::Restart type, const QString &packageID) 0256 { 0257 auto transaction = qobject_cast<Transaction*>(sender()); 0258 if (transaction->property("restartType").isNull()) { 0259 transaction->setProperty("restartType", qVariantFromValue(type)); 0260 } else { 0261 Transaction::Restart oldType; 0262 oldType = transaction->property("restartType").value<Transaction::Restart>(); 0263 int old = PackageImportance::restartImportance(oldType); 0264 int newer = PackageImportance::restartImportance(type); 0265 // Check to see which one is more important 0266 if (newer > old) { 0267 transaction->setProperty("restartType", qVariantFromValue(type)); 0268 } 0269 } 0270 0271 if (!Transaction::packageName(packageID).isEmpty()) { 0272 QStringList restartPackages = transaction->property("restartPackages").toStringList(); 0273 restartPackages << Transaction::packageName(packageID); 0274 transaction->setProperty("restartPackages", restartPackages); 0275 } 0276 } 0277 0278 void TransactionWatcher::logout() 0279 { 0280 auto notify = qobject_cast<KNotification*>(sender()); 0281 Transaction::Restart restartType; 0282 restartType = notify->property("restartType").value<Transaction::Restart>(); 0283 0284 KWorkSpace::ShutdownType shutdownType; 0285 switch (restartType) { 0286 case Transaction::RestartSystem: 0287 case Transaction::RestartSecuritySystem: 0288 // The restart type was system 0289 shutdownType = KWorkSpace::ShutdownTypeReboot; 0290 break; 0291 case Transaction::RestartSession: 0292 case Transaction::RestartSecuritySession: 0293 // The restart type was session 0294 shutdownType = KWorkSpace::ShutdownTypeLogout; 0295 break; 0296 default: 0297 qCWarning(APPER_DAEMON) << "Unknown restart type:" << restartType; 0298 return; 0299 } 0300 0301 // We call KSM server to restart or logout our system 0302 KWorkSpace::requestShutDown(KWorkSpace::ShutdownConfirmYes, 0303 shutdownType, 0304 KWorkSpace::ShutdownModeInteractive); 0305 } 0306 0307 void TransactionWatcher::watchedCanceled() 0308 { 0309 auto job = qobject_cast<TransactionJob*>(sender()); 0310 if (job->isFinished()) { 0311 job->deleteLater(); 0312 return; 0313 } 0314 0315 Transaction::Role role = job->transaction()->role(); 0316 if (role != Transaction::RoleCancel && 0317 role != Transaction::RoleUnknown) { 0318 m_tracker->unregisterJob(job); 0319 m_tracker->registerJob(job); 0320 job->start(); 0321 } 0322 } 0323 0324 void TransactionWatcher::suppressSleep(bool enable, int &inhibitCookie, const QString &reason) 0325 { 0326 if (inhibitCookie == -1) { 0327 return; 0328 } 0329 0330 if (enable) { 0331 qCDebug(APPER_DAEMON) << "Begin Suppressing Sleep"; 0332 // inhibitCookie = Solid::PowerManagement::beginSuppressingSleep(reason); 0333 if (inhibitCookie == -1) { 0334 qCDebug(APPER_DAEMON) << "Sleep suppression denied!"; 0335 } 0336 } else { 0337 qCDebug(APPER_DAEMON) << "Stop Suppressing Sleep"; 0338 // if (!Solid::PowerManagement::stopSuppressingSleep(inhibitCookie)) { 0339 qCDebug(APPER_DAEMON) << "Stop failed: invalid cookie."; 0340 // } 0341 inhibitCookie = -1; 0342 } 0343 } 0344 0345 #include "moc_TransactionWatcher.cpp"