File indexing completed on 2024-04-28 09:21:02

0001 /*
0002     SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0003     SPDX-FileCopyrightText: 2021 Harald Sitter <sitter@kde.org>
0004 */
0005 
0006 #include "NotifierTruck.h"
0007 
0008 #include <chrono>
0009 
0010 #include <QCoreApplication>
0011 #include <QFile>
0012 #include <QProcess>
0013 #include <QTimer>
0014 
0015 #include <KNotification>
0016 #include <KTerminalLauncherJob>
0017 
0018 #include "../coredump.h"
0019 
0020 using namespace std::chrono_literals;
0021 
0022 bool NotifyTruck::handle(const Coredump &dump)
0023 {
0024     if (!dump.m_rawData.value(dump.keyPickup()).isEmpty()) {
0025         // Pickups are currently not supported for notify handling. The problem is that we don't know if we already
0026         // notified on a crash or not because we have no persistent storage. To fix this we'd probably need to
0027         // rejigger things substantially and insert a pickup key into the journal that the processor can then look for.
0028         // Except then the processor needs to hold on to dumps until the entire journal is processed. All a bit awkward.
0029         return false;
0030     }
0031 
0032     auto notification = new KNotification(QStringLiteral("applicationcrash"));
0033 
0034     // immediate exit signal. This gets disconnected should `activated` arrive first (in that case we
0035     // want to wait for the terminal app to start and not exit on further notification signals)
0036     QObject::connect(notification, &KNotification::closed, this, [this, notification] {
0037         notification->disconnect(this);
0038         qApp->exit(0);
0039     });
0040 
0041     if (!QFile::exists(dump.filename)) {
0042         notification->setTitle(QStringLiteral("The warpcore has gone missing"));
0043         notification->setText(QStringLiteral("%1 [%2] crashed but has no core file").arg(dump.exe, QString::number(dump.pid)));
0044     } else {
0045         notification->setTitle(QStringLiteral("He's dead, Jim"));
0046         notification->setText(QStringLiteral("%1 [%2]").arg(dump.exe, QString::number(dump.pid)));
0047         auto gdbAction = notification->addAction(QStringLiteral("gdb"));
0048 
0049         const auto pid = dump.pid;
0050 
0051         connect(gdbAction, &KNotificationAction::activated, notification, [pid, this, notification]() {
0052             notification->disconnect(this);
0053             auto job = new KTerminalLauncherJob(QStringLiteral("coredumpctl gdb %1").arg(QString::number(pid)), this);
0054             job->setProcessEnvironment(QProcessEnvironment::systemEnvironment());
0055             connect(job, &KJob::result, this, [job] {
0056                 if (job->error() != KJob::NoError) {
0057                     qWarning() << job->errorText();
0058                 }
0059                 qApp->exit(0);
0060             });
0061             job->start();
0062 
0063             // Just in case the launcher job bugs out also add a timer.
0064             auto startTimeout = new QTimer(this);
0065             startTimeout->setInterval(16s);
0066             connect(startTimeout, &QTimer::timeout, this, [] {
0067                 qApp->exit(0);
0068             });
0069             startTimeout->start();
0070         });
0071     }
0072 
0073     notification->setFlags(KNotification::DefaultEvent | KNotification::SkipGrouping);
0074     notification->sendEvent();
0075 
0076     // KNotification internally depends on an eventloop to communicate over dbus. We therefore start the
0077     // eventloop here. ::handle() is expected to be blocking so this has no adverse effects.
0078     // The eventloop is either exited when the notification gets closed or when the debugger has started.
0079     qApp->exec();
0080     return true;
0081 }
0082 
0083 #include "moc_NotifierTruck.cpp"