1
/***************************************************************************
2
* Copyright (C) 2008-2011 by Daniel Nicoletti *
5
* This program is free software; you can redistribute it and/or modify *
6
* it under the terms of the GNU General Public License as published by *
7
* the Free Software Foundation; either version 2 of the License, or *
8
* (at your option) any later version. *
10
* This program is distributed in the hope that it will be useful, *
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13
* GNU General Public License for more details. *
15
* You should have received a copy of the GNU General Public License *
16
* along with this program; see the file COPYING. If not, write to *
17
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, *
18
* Boston, MA 02110-1301, USA. *
19
***************************************************************************/
21
#include "TransactionWatcher.h"
23
#include "TransactionJob.h"
25
#include <PkStrings.h>
27
#include <PackageImportance.h>
29
#include <KNotification>
31
#include <KMessageBox>
33
#include <kworkspace/kworkspace.h>
35
#include <Solid/PowerManagement>
36
#include <QtDBus/QDBusMessage>
37
#include <QtDBus/QDBusConnection>
43
Q_DECLARE_METATYPE(Transaction::Error)
45
TransactionWatcher::TransactionWatcher(bool packagekitIsRunning, QObject *parent) :
49
m_tracker = new KUiServerJobTracker(this);
51
// keep track of new transactions
52
connect(Daemon::global(), SIGNAL(transactionListChanged(QStringList)),
53
this, SLOT(transactionListChanged(QStringList)));
55
// if PackageKit is running check to see if there are running transactons already
56
if (packagekitIsRunning) {
57
// here we check whether a transaction job should be created or not
58
QList<QDBusObjectPath> paths = Daemon::global()->getTransactionList();
60
foreach (const QDBusObjectPath &path, paths) {
63
transactionListChanged(tids);
67
TransactionWatcher::~TransactionWatcher()
69
// release any cookie that we might have
70
suppressSleep(false, m_inhibitCookie);
73
void TransactionWatcher::transactionListChanged(const QStringList &tids)
75
kDebug() << tids.size();
76
if (!tids.isEmpty()) {
77
foreach (const QString &tid, tids) {
78
watchTransaction(QDBusObjectPath(tid), false);
81
// There is no current transaction, delete the jobs
82
foreach (TransactionJob *job, m_transactionJob) {
83
job->transactionDestroyed();
87
// Avoid leaks delete the jobs
88
foreach (Transaction *transaction, m_transactions) {
89
transaction->deleteLater();
91
m_transactions.clear();
92
m_transactionJob.clear();
94
// release any cookie that we might have
95
suppressSleep(false, m_inhibitCookie);
99
void TransactionWatcher::watchTransaction(const QDBusObjectPath &tid, bool interactive)
101
Transaction *transaction;
102
if (!m_transactions.contains(tid)) {
103
// Check if the current transaction is still the same
104
transaction = new Transaction(tid, this);
105
if (transaction->error()) {
106
qWarning() << "Could not create a transaction for the path:" << tid.path();
111
// Store the transaction id
112
m_transactions[tid] = transaction;
114
Transaction::Role role = transaction->role();
115
if (role == Transaction::RoleInstallPackages ||
116
role == Transaction::RoleInstallFiles ||
117
role == Transaction::RoleRemovePackages ||
118
role == Transaction::RoleUpdatePackages ||
119
role == Transaction::RoleUpgradeSystem) {
120
// AVOID showing messages and restart requires when
121
// the user was just simulating an instalation
122
// TODO fix yum backend
123
connect(transaction, SIGNAL(message(PackageKit::Transaction::Message,QString)),
124
this, SLOT(message(PackageKit::Transaction::Message,QString)));
125
connect(transaction, SIGNAL(requireRestart(PackageKit::Transaction::Restart,QString)),
126
this, SLOT(requireRestart(PackageKit::Transaction::Restart,QString)));
128
// Don't let the system sleep while doing some sensible actions
129
suppressSleep(true, m_inhibitCookie, PkStrings::action(role));
131
connect(transaction, SIGNAL(changed()), this, SLOT(transactionChanged()));
132
connect(transaction, SIGNAL(finished(PackageKit::Transaction::Exit,uint)),
133
this, SLOT(finished(PackageKit::Transaction::Exit)));
135
transaction = m_transactions[tid];
138
// force the first changed or create a TransactionJob
139
transactionChanged(transaction, interactive);
142
void TransactionWatcher::finished(PackageKit::Transaction::Exit exit)
144
// check if the transaction emitted any require restart
145
Transaction *transaction = qobject_cast<Transaction*>(sender());
146
QDBusObjectPath tid = transaction->tid();
147
disconnect(transaction, SIGNAL(changed()), this, SLOT(transactionChanged()));
148
m_transactions.remove(tid);
149
m_transactionJob.remove(tid);
151
if (exit == Transaction::ExitSuccess && !transaction->property("restartType").isNull()) {
152
Transaction::Restart type = transaction->property("restartType").value<Transaction::Restart>();
153
QStringList restartPackages = transaction->property("restartPackages").toStringList();
155
// Create the notification about this transaction
156
KNotification *notify = new KNotification("RestartRequired", 0, KNotification::Persistent);
157
connect(notify, SIGNAL(activated(uint)), this, SLOT(logout()));
158
notify->setComponentData(KComponentData("apperd"));
159
notify->setProperty("restartType", qVariantFromValue(type));
160
notify->setPixmap(PkIcons::restartIcon(type).pixmap(KPK_ICON_SIZE, KPK_ICON_SIZE));
161
notify->setTitle(PkStrings::restartType(type));
163
// Create a readable text with package names that required the restart
164
if (!restartPackages.isEmpty()) {
165
restartPackages.removeDuplicates();
166
restartPackages.sort();
169
text = i18np("Package: %2",
171
restartPackages.size(),
172
restartPackages.join(QLatin1String(", ")));
173
notify->setText(text);
176
// TODO RestartApplication should be handled differently
178
actions << i18n("Restart");
179
notify->setActions(actions);
185
void TransactionWatcher::transactionChanged(Transaction *transaction, bool interactive)
188
transaction = qobject_cast<Transaction*>(sender());
191
QDBusObjectPath tid = transaction->tid();
193
interactive = !transaction->isCallerActive();
197
if (!m_transactionJob.contains(tid) && interactive) {
198
TransactionJob *job = new TransactionJob(transaction, this);
199
connect(transaction, SIGNAL(errorCode(PackageKit::Transaction::Error,QString)),
200
this, SLOT(errorCode(PackageKit::Transaction::Error,QString)));
201
connect(job, SIGNAL(canceled()), this, SLOT(watchedCanceled()));
202
m_tracker->registerJob(job);
203
m_transactionJob[tid] = job;
208
void TransactionWatcher::message(PackageKit::Transaction::Message type, const QString &message)
210
KNotification *notify;
211
notify = new KNotification("TransactionMessage", 0, KNotification::Persistent);
212
notify->setComponentData(KComponentData("apperd"));
213
notify->setTitle(PkStrings::message(type));
214
notify->setText(message);
216
notify->setPixmap(KIcon("dialog-warning").pixmap(KPK_ICON_SIZE, KPK_ICON_SIZE));
220
void TransactionWatcher::errorCode(PackageKit::Transaction::Error err, const QString &details)
222
KNotification *notify;
223
notify = new KNotification("TransactionError", 0, KNotification::Persistent);
224
notify->setComponentData(KComponentData("apperd"));
225
notify->setTitle(PkStrings::error(err));
226
notify->setText(PkStrings::errorMessage(err));
227
notify->setProperty("ErrorType", QVariant::fromValue(err));
228
notify->setProperty("Details", details);
231
actions << i18n("Details");
232
notify->setActions(actions);
233
notify->setPixmap(KIcon("dialog-error").pixmap(KPK_ICON_SIZE, KPK_ICON_SIZE));
234
connect(notify, SIGNAL(activated(uint)),
235
this, SLOT(errorActivated(uint)));
239
void TransactionWatcher::errorActivated(uint action)
241
KNotification *notify = qobject_cast<KNotification*>(sender());
243
// if the user clicked "Details"
245
Transaction::Error error = notify->property("ErrorType").value<Transaction::Error>();
246
QString details = notify->property("Details").toString();
247
KMessageBox::detailedSorry(0,
248
PkStrings::errorMessage(error),
249
details.replace('\n', "<br />"),
250
PkStrings::error(error),
251
KMessageBox::Notify);
257
void TransactionWatcher::requireRestart(PackageKit::Transaction::Restart type, const QString &packageID)
259
Transaction *transaction = qobject_cast<Transaction*>(sender());
260
if (transaction->property("restartType").isNull()) {
261
transaction->setProperty("restartType", qVariantFromValue(type));
263
Transaction::Restart oldType;
264
oldType = transaction->property("restartType").value<Transaction::Restart>();
265
int old = PackageImportance::restartImportance(oldType);
266
int newer = PackageImportance::restartImportance(type);
267
// Check to see which one is more important
269
transaction->setProperty("restartType", qVariantFromValue(type));
273
if (!Transaction::packageName(packageID).isEmpty()) {
274
QStringList restartPackages = transaction->property("restartPackages").toStringList();
275
restartPackages << Transaction::packageName(packageID);
276
transaction->setProperty("restartPackages", restartPackages);
280
void TransactionWatcher::logout()
282
KNotification *notify = qobject_cast<KNotification*>(sender());
283
Transaction::Restart restartType;
284
restartType = notify->property("restartType").value<Transaction::Restart>();
286
KWorkSpace::ShutdownType shutdownType;
287
switch (restartType) {
288
case Transaction::RestartSystem:
289
case Transaction::RestartSecuritySystem:
290
// The restart type was system
291
shutdownType = KWorkSpace::ShutdownTypeReboot;
293
case Transaction::RestartSession:
294
case Transaction::RestartSecuritySession:
295
// The restart type was session
296
shutdownType = KWorkSpace::ShutdownTypeLogout;
299
kWarning() << "Unknown restart type:" << restartType;
303
// We call KSM server to restart or logout our system
304
KWorkSpace::requestShutDown(KWorkSpace::ShutdownConfirmYes,
306
KWorkSpace::ShutdownModeInteractive);
309
void TransactionWatcher::watchedCanceled()
311
TransactionJob *job = qobject_cast<TransactionJob*>(sender());
312
if (job->isFinished()) {
317
Transaction::Role role = job->transaction()->role();
318
if (role != Transaction::RoleCancel &&
319
role != Transaction::RoleUnknown) {
320
m_tracker->unregisterJob(job);
321
m_tracker->registerJob(job);
326
void TransactionWatcher::suppressSleep(bool enable, int &inhibitCookie, const QString &reason)
328
if (inhibitCookie == -1) {
333
kDebug() << "Begin Suppressing Sleep";
334
inhibitCookie = Solid::PowerManagement::beginSuppressingSleep(reason);
335
if (inhibitCookie == -1) {
336
kDebug() << "Sleep suppression denied!";
339
kDebug() << "Stop Suppressing Sleep";
340
if (!Solid::PowerManagement::stopSuppressingSleep(inhibitCookie)) {
341
kDebug() << "Stop failed: invalid cookie.";