A bunch of improvements

tsimonq2/null-packages-fix
Aaron Rainbolt 12 months ago
parent 29c1eb8abb
commit 1ccbb8b1a0

@ -32,6 +32,8 @@ set(PROJECT_SOURCES
conffilehandlerdialog.h conffilehandlerdialog.h
conffilehandlerdialog.cpp conffilehandlerdialog.cpp
conffilehandlerdialog.ui conffilehandlerdialog.ui
ipcfilewatcher.h
ipcfilewatcher.cpp
${TS_FILES} ${TS_FILES}
) )
@ -60,6 +62,18 @@ install(TARGETS lubuntu-update
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
) )
# Yes, we are hardcoding a path here. Yes, it's ugly. However, the program
# will attempt to run this script as root, and I do NOT want to make it try to
# "find" the script and execute the first thing it finds that has the right
# name, that sounds like a security breach waiting to happen. It will only
# execute lubuntu-update-backend from /usr/libexec, and if something malicious
# has gotten into /usr/libexec you're compromised anyway, so not much to worry
# about. Therefore we always, *always* install this script into /usr/libexec/,
# no matter where the rest of the program goes.
install(FILES lubuntu-update-backend DESTINATION /usr/libexec/)
install(FILES lubuntu-update.desktop DESTINATION ${CMAKE_INSTALL_PREFIX}/share/applications/)
if(QT_VERSION_MAJOR EQUAL 6) if(QT_VERSION_MAJOR EQUAL 6)
qt_finalize_executable(lubuntu-update) qt_finalize_executable(lubuntu-update)
endif() endif()

@ -14,8 +14,16 @@ cmake ..
make -j$(nproc) make -j$(nproc)
``` ```
To use, copy the lubuntu-update-backend script to /usr/bin/lubuntu-update-backend, then compile and run the updater. It is highly recommended that you **do not** install the updater all the way into your system with `sudo make install` or similar. To use, copy the lubuntu-update-backend script to /usr/libexec/lubuntu-update-backend, then compile and run the updater. It is highly recommended that you **do not** install the updater all the way into your system with `sudo make install` or similar.
It is highly recommended that you use a Lubuntu virtual machine for testing and development. Use the latest development release if at all possible, unless you know you need to test an earlier release. It is highly recommended that you use a Lubuntu virtual machine for testing and development. Use the latest development release if at all possible, unless you know you need to test an earlier release.
Qt Creator is recommended for editing the code. It is present in Ubuntu's official repos and can be installed using `sudo apt install qtcreator`. Qt Creator is recommended for editing the code. It is present in Ubuntu's official repos and can be installed using `sudo apt install qtcreator`.
## Missing features
* The Details button is hidden and does nothing. Eventually it should display a list of packages, the old version of them, the new version of them, and a link to their Launchpad page.
* There's no support for release upgrading. This is currently unnecessary as this updater is only going to be shipped in Noble and later, but it will become a big deal in the (potentially near) future.
* There's no support for doing an `apt update` for checking for recent updates. This seems rather important *now*.
* Most of the internal strings aren't translatable...
* ...and the bit of translation support there is, is totally untested and quite possibly not functioning properly. Borrow from what we did with lubuntu-installer-prompt to fix this.

@ -28,7 +28,7 @@ void AptManager::applyFullUpgrade()
aptProcess = new QProcess(); aptProcess = new QProcess();
aptProcess->setProgram("/usr/bin/lxqt-sudo"); aptProcess->setProgram("/usr/bin/lxqt-sudo");
// Note that the lubuntu-update-backend script sets LC_ALL=C in it already, so we don't need to add that here. // Note that the lubuntu-update-backend script sets LC_ALL=C in it already, so we don't need to add that here.
aptProcess->setArguments(QStringList() << "/usr/bin/lubuntu-update-backend" << "doupdate"); aptProcess->setArguments(QStringList() << "/usr/libexec/lubuntu-update-backend" << "doupdate");
aptProcess->setProcessChannelMode(QProcess::MergedChannels); aptProcess->setProcessChannelMode(QProcess::MergedChannels);
QObject::connect(aptProcess, &QProcess::readyRead, this, &AptManager::handleProcessBuffer); QObject::connect(aptProcess, &QProcess::readyRead, this, &AptManager::handleProcessBuffer);
QObject::connect(aptProcess, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, &AptManager::handleProcessBuffer); QObject::connect(aptProcess, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, &AptManager::handleProcessBuffer);

@ -0,0 +1,38 @@
#include "ipcfilewatcher.h"
#include <QApplication>
#include <QDir>
#include <QFile>
#include <QFileSystemWatcher>
#include <QDebug>
IPCFileWatcher::IPCFileWatcher(QObject *parent)
: QObject{parent}
{
QDir targetDir("/dev/shm/lubuntu-update");
bool couldRemove = targetDir.removeRecursively();
if (!couldRemove) {
qCritical() << "Could not clear IPC directory. Ensure that /dev/shm and /dev/shm/lubuntu-update are world-readable and world-writable.";
initFailed = true;
return;
}
targetDir.mkdir("/dev/shm/lubuntu-update");
QFileSystemWatcher *watcher = new QFileSystemWatcher(QStringList() << "/dev/shm/lubuntu-update");
connect(watcher, &QFileSystemWatcher::directoryChanged, this, &IPCFileWatcher::checkForShowWindowFile);
initFailed = false;
}
bool IPCFileWatcher::didInitFail()
{
return initFailed;
}
void IPCFileWatcher::checkForShowWindowFile()
{
QFile flagFile("/dev/shm/lubuntu-update/lubuntu-update-show-win");
if (flagFile.exists()) {
flagFile.remove();
emit showWindowFlagDetected();
}
}

@ -0,0 +1,22 @@
#ifndef IPCFILEWATCHER_H
#define IPCFILEWATCHER_H
#include <QObject>
class IPCFileWatcher : public QObject
{
Q_OBJECT
public:
explicit IPCFileWatcher(QObject *parent = nullptr);
bool didInitFail();
signals:
void showWindowFlagDetected();
private:
bool initFailed;
void checkForShowWindowFile();
};
#endif // IPCFILEWATCHER_H

@ -0,0 +1,11 @@
[Desktop Entry]
Exec=/usr/bin/lubuntu-update
Name=Lubuntu Update
GenericName=Lubuntu Update
Comment=View available updates and optionally install them
Icon=system-software-update
Type=Application
Version=0.1
Categories=System;Settings;
Keywords=upgrade;update
Terminal=false

@ -1,3 +1,4 @@
#include "ipcfilewatcher.h"
#include "orchestrator.h" #include "orchestrator.h"
#include "mainwindow.h" #include "mainwindow.h"
#include "conffilehandlerdialog.h" #include "conffilehandlerdialog.h"
@ -5,6 +6,7 @@
#include <QApplication> #include <QApplication>
#include <QDialog> #include <QDialog>
#include <QLocale> #include <QLocale>
#include <QProcess>
#include <QTranslator> #include <QTranslator>
int main(int argc, char *argv[]) int main(int argc, char *argv[])
@ -20,9 +22,44 @@ int main(int argc, char *argv[])
break; break;
} }
} }
/*
* If Lubuntu Update is already running, create
* /dev/shm/lubuntu-update/lubuntu-update-show-win and exit. This will
* trigger the existing process to pop up a window.
*/
QProcess procDetector;
procDetector.setProgram("/usr/bin/bash");
procDetector.setArguments(QStringList() << "-c" << "ps axo comm | grep lubuntu-update");
procDetector.start();
procDetector.waitForFinished();
QString procDetectResult = procDetector.readAllStandardOutput();
procDetectResult = procDetectResult.trimmed();
QStringList procDetectResultList = procDetectResult.split('\n');
if (procDetectResultList.count() > 1) {
QFile flagFile("/dev/shm/lubuntu-update/lubuntu-update-show-win");
flagFile.open(QFile::WriteOnly);
flagFile.close();
return 0;
}
// Don't want the updater to stop just because the user closed it :P // Don't want the updater to stop just because the user closed it :P
a.setQuitOnLastWindowClosed(false); a.setQuitOnLastWindowClosed(false);
/*
* IPCFileWatcher just watches the /dev/shm/lubuntu-update folder for the
* creation of a lubuntu-update-show-win file. If it detects it, it
* immediately deletes it and emits a signal. This is then used later to
* cause the updater window to pop up.
*/
IPCFileWatcher *p = new IPCFileWatcher();
if (p->didInitFail()) {
return 1;
}
/* /*
* As this is a background process, we don't pop up any window upon * As this is a background process, we don't pop up any window upon
* startup. An Orchestrator object periodically checks to see if new * startup. An Orchestrator object periodically checks to see if new
@ -37,6 +74,8 @@ int main(int argc, char *argv[])
Orchestrator *o = new Orchestrator(); Orchestrator *o = new Orchestrator();
QObject::connect(p, &IPCFileWatcher::showWindowFlagDetected, o, &Orchestrator::displayUpdater);
/* /*
* This is an artifact from testing the conffile handler window. You can * This is an artifact from testing the conffile handler window. You can
* uncomment this and rebuild lubuntu-update in order to test the conffile * uncomment this and rebuild lubuntu-update in order to test the conffile

@ -17,6 +17,9 @@ MainWindow::MainWindow(QWidget *parent)
ui->progressBar->setVisible(false); ui->progressBar->setVisible(false);
ui->logView->setVisible(false); ui->logView->setVisible(false);
// FIXME: Implement the Details screen and attach this button to it rather than disabling it.
ui->detailsButton->setVisible(false);
connect(ui->installButton, &QPushButton::clicked, this, &MainWindow::onInstallButtonClicked); connect(ui->installButton, &QPushButton::clicked, this, &MainWindow::onInstallButtonClicked);
connect(ui->closeButton, &QPushButton::clicked, this, &MainWindow::onCloseButtonClicked); connect(ui->closeButton, &QPushButton::clicked, this, &MainWindow::onCloseButtonClicked);
connect(aptManager, &AptManager::updateComplete, this, &MainWindow::onUpdateCompleted); connect(aptManager, &AptManager::updateComplete, this, &MainWindow::onUpdateCompleted);
@ -36,11 +39,15 @@ void MainWindow::setUpdateInfo(QList<QStringList> updateInfo)
// The progress bar and log view are shown after the user chooses to begin installing updates // The progress bar and log view are shown after the user chooses to begin installing updates
ui->progressBar->setVisible(false); ui->progressBar->setVisible(false);
ui->logView->setVisible(false); ui->logView->setVisible(false);
ui->installButton->setEnabled(true);
ui->detailsButton->setEnabled(true); ui->detailsButton->setEnabled(true);
ui->closeButton->setEnabled(true); ui->closeButton->setEnabled(true);
ui->installButton->setEnabled(false); // Correct, it starts out false, we turn it to true if there are any updates.
for (int i = 0;i < 4;i++) { for (int i = 0;i < 4;i++) {
if (updateInfo[i].count() > 0) {
ui->installButton->setEnabled(true);
}
QTreeWidgetItem *installItem; QTreeWidgetItem *installItem;
switch (i) { switch (i) {
case 0: case 0:
@ -69,6 +76,18 @@ void MainWindow::setUpdateInfo(QList<QStringList> updateInfo)
QString::number(updateInfo[4].count()))); QString::number(updateInfo[4].count())));
} }
bool MainWindow::isLockedOpen()
{
/*
* If the Close button is disabled, we do NOT want the window to close
* under virtually any circumstances. This allows the Orchestrator to
* determine whether or not it is save to close and re-open the window
* from the outside.
*/
return !ui->closeButton->isEnabled();
}
void MainWindow::closeEvent(QCloseEvent *event) void MainWindow::closeEvent(QCloseEvent *event)
{ {
/* /*
@ -99,6 +118,7 @@ void MainWindow::onCloseButtonClicked()
void MainWindow::onUpdateCompleted() void MainWindow::onUpdateCompleted()
{ {
ui->closeButton->setEnabled(true); ui->closeButton->setEnabled(true);
ui->installButton->setEnabled(false);
ui->progressBar->setVisible(false); ui->progressBar->setVisible(false);
ui->statLabel->setText("Update installation complete."); ui->statLabel->setText("Update installation complete.");
emit updatesInstalled(); // this tells the orchestrator to hide the tray icon emit updatesInstalled(); // this tells the orchestrator to hide the tray icon

@ -21,6 +21,7 @@ public:
MainWindow(QWidget *parent = nullptr); MainWindow(QWidget *parent = nullptr);
~MainWindow(); ~MainWindow();
void setUpdateInfo(QList<QStringList> updateInfo); void setUpdateInfo(QList<QStringList> updateInfo);
bool isLockedOpen();
signals: signals:
void updatesInstalled(); void updatesInstalled();

@ -13,7 +13,7 @@ Orchestrator::Orchestrator(QObject *parent)
trayIcon = new QSystemTrayIcon(); // this is shown to the user to offer updates trayIcon = new QSystemTrayIcon(); // this is shown to the user to offer updates
connect(checkTimer, &QTimer::timeout, this, &Orchestrator::checkForUpdates); connect(checkTimer, &QTimer::timeout, this, &Orchestrator::checkForUpdates);
connect(trayIcon, &QSystemTrayIcon::activated, this, [this](){this->displayUpdater(updateInfo);}); connect(trayIcon, &QSystemTrayIcon::activated, this, &Orchestrator::displayUpdater);
connect(&updaterWindow, &MainWindow::updatesInstalled, this, &Orchestrator::handleUpdatesInstalled); connect(&updaterWindow, &MainWindow::updatesInstalled, this, &Orchestrator::handleUpdatesInstalled);
checkTimer->start(21600000); // check four times a day, at least one of those times unattended-upgrades should have refreshed the apt database checkTimer->start(21600000); // check four times a day, at least one of those times unattended-upgrades should have refreshed the apt database
@ -39,9 +39,12 @@ void Orchestrator::checkForUpdates()
} }
} }
void Orchestrator::displayUpdater(QList<QStringList> updateInfo) void Orchestrator::displayUpdater()
{ {
if (!updaterWindow.isLockedOpen()) {
if (!updaterWindow.isVisible()) { if (!updaterWindow.isVisible()) {
updaterWindow.hide();
}
updaterWindow.setUpdateInfo(updateInfo); updaterWindow.setUpdateInfo(updateInfo);
updaterWindow.show(); updaterWindow.show();
} }
@ -49,5 +52,9 @@ void Orchestrator::displayUpdater(QList<QStringList> updateInfo)
void Orchestrator::handleUpdatesInstalled() void Orchestrator::handleUpdatesInstalled()
{ {
// We can't clear the updateInfo list directly as MainWindow::setUpdateInfo requires that it contains five inner lists (even if those lists are all empty).
for (int i = 0;i < 5;i++) {
updateInfo[i].clear();
}
trayIcon->hide(); trayIcon->hide();
} }

@ -15,9 +15,11 @@ class Orchestrator : public QObject
public: public:
explicit Orchestrator(QObject *parent = nullptr); explicit Orchestrator(QObject *parent = nullptr);
public slots:
void displayUpdater();
private slots: private slots:
void checkForUpdates(); void checkForUpdates();
void displayUpdater(QList<QStringList> updateInfo);
void handleUpdatesInstalled(); void handleUpdatesInstalled();
private: private:

Loading…
Cancel
Save