File indexing completed on 2024-04-14 05:38:14
0001 /* 0002 SPDX-FileCopyrightText: 2018-2020 Harald Sitter <sitter@kde.org> 0003 SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0004 */ 0005 0006 #include "upgraderprocess.h" 0007 0008 #include <KLocalizedString> 0009 #include <KOSRelease> 0010 #include <KTitleWidget> 0011 0012 #include <QDialog> 0013 #include <QDialogButtonBox> 0014 #include <QIcon> 0015 #include <QProcess> 0016 #include <QTextEdit> 0017 #include <QVBoxLayout> 0018 0019 #include "debug.h" 0020 #include "upgraderwatcher.h" 0021 0022 void UpgraderProcess::setUseDevel(bool useDevel) 0023 { 0024 m_useDevel = useDevel; 0025 } 0026 0027 void UpgraderProcess::run() 0028 { 0029 auto process = new QProcess(this); 0030 connect(process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), 0031 this, [this](){ 0032 m_waiting = false; 0033 emit notPending(); 0034 deleteLater(); 0035 }); 0036 0037 process->setProcessChannelMode(QProcess::MergedChannels); 0038 connect(process, &QProcess::readyReadStandardOutput, 0039 this, [this, process]() { 0040 if (!NOTIFIER().isDebugEnabled() && !m_waiting) { 0041 return; 0042 } 0043 0044 const QString newOutput = QString::fromUtf8(process->readAllStandardOutput()); 0045 // route this through format string so newlines are preserved 0046 qCDebug(NOTIFIER, "do-release-upgrader: %s\n", newOutput.toUtf8().constData()); 0047 if (m_waiting) { 0048 m_output += newOutput; 0049 } 0050 }); 0051 0052 // Monitor dbus for the higher level UIs to appear. 0053 // If the proc finishes before the dbus magic happens it likely crapped out in early 0054 // checks which have zero UI backing, meaning we need to display stdout in a dialog. 0055 auto unexpectedConnection = connect(process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), 0056 this, &UpgraderProcess::onUnexpectedFinish); 0057 connect(UpgraderWatcher::self(), &UpgraderWatcher::upgraderRunning, 0058 this, [this, unexpectedConnection]() { 0059 m_waiting = false; 0060 emit notPending(); 0061 disconnect(unexpectedConnection); 0062 }); 0063 0064 // pkexec is being difficult. It will refuse to auth a startDetached service 0065 // because it won't have a parent and parentless commands are not allowed 0066 // to auth. 0067 // Instead hold on to the process. 0068 // For future reference: another approach is to sh -c and hold 0069 // do-release-upgrade as a fork of that sh. 0070 auto args = QStringList({ 0071 QStringLiteral("-m"), QStringLiteral("desktop"), 0072 QStringLiteral("-f"), QStringLiteral("DistUpgradeViewKDE") 0073 }); 0074 if (m_useDevel) { 0075 args << QStringLiteral("--devel-release"); 0076 } 0077 0078 qCDebug(NOTIFIER) << "Starting do-release-upgrade"; 0079 process->start(QStringLiteral("do-release-upgrade"), args); 0080 } 0081 0082 void UpgraderProcess::onUnexpectedFinish(int code) 0083 { 0084 // If the process finished within some seconds something is probably broken 0085 // in the bootstrap. Display the output. 0086 // Notably the upgrader exit(1) when it detects pending updates on apt and will only 0087 // inform the user through stdout. It's very crappy UX. 0088 0089 if (code == 0) { 0090 qCWarning(NOTIFIER) << "Unexpected early exit but ignoring because it was code 0!"; 0091 return; 0092 } 0093 0094 qCDebug(NOTIFIER) << "Probable failure" << code; 0095 0096 QDialog dialog; 0097 dialog.setWindowIcon(QIcon::fromTheme(QStringLiteral("system-software-update"))); 0098 dialog.setWindowTitle(i18nc("@title/window upgrade failure dialog", "Upgrade Failed")); 0099 auto layout = new QVBoxLayout; 0100 dialog.setLayout(layout); 0101 0102 auto title = new KTitleWidget(&dialog); 0103 title->setText(i18nc("@title title widget above process output", 0104 "Upgrade failed with the following output:")); 0105 layout->addWidget(title); 0106 0107 auto editor = new QTextEdit(&dialog); 0108 editor->setReadOnly(true); 0109 editor->setText(m_output.replace(QStringLiteral("ubuntu"), QStringLiteral("neon"), Qt::CaseInsensitive)); 0110 layout->addWidget(editor); 0111 0112 auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Close, &dialog); 0113 layout->addWidget(buttonBox); 0114 connect(buttonBox, &QDialogButtonBox::rejected, &dialog, &QDialog::reject); 0115 0116 dialog.exec(); 0117 }