A bunch of improvements
This commit is contained in:
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()
|
||||||
|
10
README.md
10
README.md
@ -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);
|
||||||
|
38
ipcfilewatcher.cpp
Normal file
38
ipcfilewatcher.cpp
Normal file
@ -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();
|
||||||
|
}
|
||||||
|
}
|
22
ipcfilewatcher.h
Normal file
22
ipcfilewatcher.h
Normal file
@ -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
|
11
lubuntu-update.desktop
Normal file
11
lubuntu-update.desktop
Normal file
@ -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
|
39
main.cpp
39
main.cpp
@ -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.isVisible()) {
|
if (!updaterWindow.isLockedOpen()) {
|
||||||
|
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…
x
Reference in New Issue
Block a user