~ubuntu-branches/debian/stretch/apper/stretch

« back to all changes in this revision

Viewing changes to .pc/01_new-pk-compat.diff/apperd/TransactionWatcher.cpp

  • Committer: Package Import Robot
  • Author(s): Matthias Klumpp
  • Date: 2013-06-24 14:32:36 UTC
  • mfrom: (8.1.1 experimental)
  • Revision ID: package-import@ubuntu.com-20130624143236-8259lpdjgnrjxiik
Tags: 0.8.0-2
* Upload to unstable (Closes: #696585, #697996, #708090)
* Add patch to compile against new QPK API

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/***************************************************************************
 
2
 *   Copyright (C) 2008-2011 by Daniel Nicoletti                           *
 
3
 *   dantti12@gmail.com                                                    *
 
4
 *                                                                         *
 
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.                                   *
 
9
 *                                                                         *
 
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.                          *
 
14
 *                                                                         *
 
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
 ***************************************************************************/
 
20
 
 
21
#include "TransactionWatcher.h"
 
22
 
 
23
#include "TransactionJob.h"
 
24
 
 
25
#include <PkStrings.h>
 
26
#include <PkIcons.h>
 
27
#include <PackageImportance.h>
 
28
 
 
29
#include <KNotification>
 
30
#include <KLocale>
 
31
#include <KMessageBox>
 
32
 
 
33
#include <kworkspace/kworkspace.h>
 
34
 
 
35
#include <Solid/PowerManagement>
 
36
#include <QtDBus/QDBusMessage>
 
37
#include <QtDBus/QDBusConnection>
 
38
 
 
39
#include <KDebug>
 
40
 
 
41
#include <Daemon>
 
42
 
 
43
Q_DECLARE_METATYPE(Transaction::Error)
 
44
 
 
45
TransactionWatcher::TransactionWatcher(bool packagekitIsRunning, QObject *parent) :
 
46
    QObject(parent),
 
47
    m_inhibitCookie(-1)
 
48
{
 
49
    m_tracker = new KUiServerJobTracker(this);
 
50
 
 
51
    // keep track of new transactions
 
52
    connect(Daemon::global(), SIGNAL(transactionListChanged(QStringList)),
 
53
            this, SLOT(transactionListChanged(QStringList)));
 
54
 
 
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();
 
59
        QStringList tids;
 
60
        foreach (const QDBusObjectPath &path, paths) {
 
61
            tids << path.path();
 
62
        }
 
63
        transactionListChanged(tids);
 
64
    }
 
65
}
 
66
 
 
67
TransactionWatcher::~TransactionWatcher()
 
68
{
 
69
    // release any cookie that we might have
 
70
    suppressSleep(false, m_inhibitCookie);
 
71
}
 
72
 
 
73
void TransactionWatcher::transactionListChanged(const QStringList &tids)
 
74
{
 
75
    kDebug() << tids.size();
 
76
    if (!tids.isEmpty()) {
 
77
        foreach (const QString &tid, tids) {
 
78
            watchTransaction(QDBusObjectPath(tid), false);
 
79
        }
 
80
    } else {
 
81
        // There is no current transaction, delete the jobs
 
82
        foreach (TransactionJob *job, m_transactionJob) {
 
83
            job->transactionDestroyed();
 
84
            job->deleteLater();
 
85
        }
 
86
 
 
87
        // Avoid leaks delete the jobs
 
88
        foreach (Transaction *transaction, m_transactions) {
 
89
            transaction->deleteLater();
 
90
        }
 
91
        m_transactions.clear();
 
92
        m_transactionJob.clear();
 
93
 
 
94
        // release any cookie that we might have
 
95
        suppressSleep(false, m_inhibitCookie);
 
96
    }
 
97
}
 
98
 
 
99
void TransactionWatcher::watchTransaction(const QDBusObjectPath &tid, bool interactive)
 
100
{
 
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();
 
107
            delete transaction;
 
108
            return;
 
109
        }
 
110
 
 
111
        // Store the transaction id
 
112
        m_transactions[tid] = transaction;
 
113
 
 
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)));
 
127
 
 
128
            // Don't let the system sleep while doing some sensible actions
 
129
            suppressSleep(true, m_inhibitCookie, PkStrings::action(role));
 
130
        }
 
131
        connect(transaction, SIGNAL(changed()), this, SLOT(transactionChanged()));
 
132
        connect(transaction, SIGNAL(finished(PackageKit::Transaction::Exit,uint)),
 
133
                this, SLOT(finished(PackageKit::Transaction::Exit)));
 
134
    } else {
 
135
        transaction = m_transactions[tid];
 
136
    }
 
137
 
 
138
    // force the first changed or create a TransactionJob
 
139
    transactionChanged(transaction, interactive);
 
140
}
 
141
 
 
142
void TransactionWatcher::finished(PackageKit::Transaction::Exit exit)
 
143
{
 
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);
 
150
 
 
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();
 
154
 
 
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));
 
162
 
 
163
        // Create a readable text with package names that required the restart
 
164
        if (!restartPackages.isEmpty()) {
 
165
            restartPackages.removeDuplicates();
 
166
            restartPackages.sort();
 
167
 
 
168
            QString text;
 
169
            text = i18np("Package: %2",
 
170
                         "Packages: %2",
 
171
                         restartPackages.size(),
 
172
                         restartPackages.join(QLatin1String(", ")));
 
173
            notify->setText(text);
 
174
        }
 
175
 
 
176
        // TODO RestartApplication should be handled differently
 
177
        QStringList actions;
 
178
        actions << i18n("Restart");
 
179
        notify->setActions(actions);
 
180
 
 
181
        notify->sendEvent();
 
182
    }
 
183
}
 
184
 
 
185
void TransactionWatcher::transactionChanged(Transaction *transaction, bool interactive)
 
186
{
 
187
    if (!transaction) {
 
188
        transaction = qobject_cast<Transaction*>(sender());
 
189
    }
 
190
 
 
191
    QDBusObjectPath tid = transaction->tid();
 
192
    if (!interactive) {
 
193
        interactive = !transaction->isCallerActive();
 
194
    }
 
195
 
 
196
    // If the
 
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;
 
204
        job->start();
 
205
    }
 
206
}
 
207
 
 
208
void TransactionWatcher::message(PackageKit::Transaction::Message type, const QString &message)
 
209
{
 
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);
 
215
 
 
216
    notify->setPixmap(KIcon("dialog-warning").pixmap(KPK_ICON_SIZE, KPK_ICON_SIZE));
 
217
    notify->sendEvent();
 
218
}
 
219
 
 
220
void TransactionWatcher::errorCode(PackageKit::Transaction::Error err, const QString &details)
 
221
{
 
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);
 
229
 
 
230
    QStringList actions;
 
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)));
 
236
    notify->sendEvent();
 
237
}
 
238
 
 
239
void TransactionWatcher::errorActivated(uint action)
 
240
{
 
241
    KNotification *notify = qobject_cast<KNotification*>(sender());
 
242
 
 
243
    // if the user clicked "Details"
 
244
    if (action == 1) {
 
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);
 
252
    }
 
253
 
 
254
    notify->close();
 
255
}
 
256
 
 
257
void TransactionWatcher::requireRestart(PackageKit::Transaction::Restart type, const QString &packageID)
 
258
{
 
259
    Transaction *transaction = qobject_cast<Transaction*>(sender());
 
260
    if (transaction->property("restartType").isNull()) {
 
261
        transaction->setProperty("restartType", qVariantFromValue(type));
 
262
    } else {
 
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
 
268
        if (newer > old) {
 
269
            transaction->setProperty("restartType", qVariantFromValue(type));
 
270
        }
 
271
    }
 
272
 
 
273
    if (!Transaction::packageName(packageID).isEmpty()) {
 
274
        QStringList restartPackages = transaction->property("restartPackages").toStringList();
 
275
        restartPackages << Transaction::packageName(packageID);
 
276
        transaction->setProperty("restartPackages", restartPackages);
 
277
    }
 
278
}
 
279
 
 
280
void TransactionWatcher::logout()
 
281
{
 
282
    KNotification *notify = qobject_cast<KNotification*>(sender());
 
283
    Transaction::Restart restartType;
 
284
    restartType = notify->property("restartType").value<Transaction::Restart>();
 
285
 
 
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;
 
292
        break;
 
293
    case Transaction::RestartSession:
 
294
    case Transaction::RestartSecuritySession:
 
295
        // The restart type was session
 
296
        shutdownType = KWorkSpace::ShutdownTypeLogout;
 
297
        break;
 
298
    default:
 
299
        kWarning() << "Unknown restart type:" << restartType;
 
300
        return;
 
301
    }
 
302
 
 
303
    // We call KSM server to restart or logout our system
 
304
    KWorkSpace::requestShutDown(KWorkSpace::ShutdownConfirmYes,
 
305
                                shutdownType,
 
306
                                KWorkSpace::ShutdownModeInteractive);
 
307
}
 
308
 
 
309
void TransactionWatcher::watchedCanceled()
 
310
{
 
311
    TransactionJob *job = qobject_cast<TransactionJob*>(sender());
 
312
    if (job->isFinished()) {
 
313
        job->deleteLater();
 
314
        return;
 
315
    }
 
316
 
 
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);
 
322
        job->start();
 
323
    }
 
324
}
 
325
 
 
326
void TransactionWatcher::suppressSleep(bool enable, int &inhibitCookie, const QString &reason)
 
327
{
 
328
    if (inhibitCookie == -1) {
 
329
        return;
 
330
    }
 
331
 
 
332
    if (enable) {
 
333
        kDebug() << "Begin Suppressing Sleep";
 
334
        inhibitCookie = Solid::PowerManagement::beginSuppressingSleep(reason);
 
335
        if (inhibitCookie == -1) {
 
336
            kDebug() << "Sleep suppression denied!";
 
337
        }
 
338
    } else {
 
339
        kDebug() << "Stop Suppressing Sleep";
 
340
        if (!Solid::PowerManagement::stopSuppressingSleep(inhibitCookie)) {
 
341
            kDebug() << "Stop failed: invalid cookie.";
 
342
        }
 
343
        inhibitCookie = -1;
 
344
    }
 
345
}