mirror of
https://github.com/vincentmli/bpfire.git
synced 2026-04-28 11:43:25 +02:00
pakfire: Implement feedback from mailing list discussion
- Improve lockfile test: Return immediately if lockfile is present, to prevent unnecessary and expensive "pidof" calls - Add better explanation to the log file reading command and JS - Change user interface: If no errors occurred, the page returns to the main screen (after a short delay). If an error occurred, the log output remains and a message is shown. Signed-off-by: Leo-Andres Hofmann <hofmann@leo-andres.de> Acked-by: Peter Müller <peter.mueller@ipfire.org>
This commit is contained in:
committed by
Peter Müller
parent
a02f132338
commit
4d70f59173
@@ -20,6 +20,7 @@
|
|||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
use strict;
|
use strict;
|
||||||
|
use List::Util qw(any);
|
||||||
|
|
||||||
# enable only the following on debugging purpose
|
# enable only the following on debugging purpose
|
||||||
#use warnings;
|
#use warnings;
|
||||||
@@ -54,13 +55,20 @@ if($cgiparams{'ACTION'} eq 'json-getstatus') {
|
|||||||
# Send HTTP headers
|
# Send HTTP headers
|
||||||
_start_json_output();
|
_start_json_output();
|
||||||
|
|
||||||
# Collect Pakfire status and log messages
|
# Read /var/log/messages backwards until a "Pakfire started" header is found,
|
||||||
|
# to capture all messages of the last (i.e. current) Pakfire run
|
||||||
|
my @messages = `tac /var/log/messages | sed -n '/pakfire:/{p;/Pakfire.*started/q}'`;
|
||||||
|
|
||||||
|
# Test if the log contains an error message (fastest implementation, stops at first match)
|
||||||
|
my $failure = any{ index($_, 'ERROR') != -1 } @messages;
|
||||||
|
|
||||||
|
# Collect Pakfire status
|
||||||
my %status = (
|
my %status = (
|
||||||
'running' => &_is_pakfire_busy() || "0",
|
'running' => &_is_pakfire_busy() || "0",
|
||||||
'running_since' => &General::age("$Pakfire::lockfile") || "0s",
|
'running_since' => &General::age("$Pakfire::lockfile") || "0s",
|
||||||
'reboot' => (-e "/var/run/need_reboot") || "0"
|
'reboot' => (-e "/var/run/need_reboot") || "0",
|
||||||
|
'failure' => $failure || "0"
|
||||||
);
|
);
|
||||||
my @messages = `tac /var/log/messages | sed -n '/pakfire:/{p;/Pakfire.*started/q}'`;
|
|
||||||
|
|
||||||
# Start JSON file
|
# Start JSON file
|
||||||
print "{\n";
|
print "{\n";
|
||||||
@@ -128,14 +136,18 @@ my $extraHead = <<END
|
|||||||
pakfire.i18n.load({
|
pakfire.i18n.load({
|
||||||
'working': '$Lang::tr{'pakfire working'}',
|
'working': '$Lang::tr{'pakfire working'}',
|
||||||
'finished': '$Lang::tr{'pakfire finished'}',
|
'finished': '$Lang::tr{'pakfire finished'}',
|
||||||
'since': '$Lang::tr{'since'} ', //(space is intentional)
|
'finished error': '$Lang::tr{'pakfire finished error'}',
|
||||||
|
'since': '$Lang::tr{'since'}',
|
||||||
|
|
||||||
'link_return': '<a href="$ENV{'SCRIPT_NAME'}">$Lang::tr{'pakfire return'}</a>',
|
'link_return': '<a href="$ENV{'SCRIPT_NAME'}">$Lang::tr{'pakfire return'}</a>',
|
||||||
'link_reboot': '<a href="/cgi-bin/shutdown.cgi">$Lang::tr{'needreboot'}</a>'
|
'link_reboot': '<a href="/cgi-bin/shutdown.cgi">$Lang::tr{'needreboot'}</a>'
|
||||||
});
|
});
|
||||||
|
|
||||||
// AJAX auto refresh interval
|
// AJAX auto refresh interval (in ms, default: 1000)
|
||||||
pakfire.refreshInterval = 1000;
|
//pakfire.refreshInterval = 1000;
|
||||||
|
|
||||||
|
// Enable returning to main screen (delay in ms)
|
||||||
|
pakfire.setupPageReload(true, 3000);
|
||||||
</script>
|
</script>
|
||||||
END
|
END
|
||||||
;
|
;
|
||||||
@@ -276,6 +288,7 @@ if(&_is_pakfire_busy()) {
|
|||||||
<!-- Pakfire log messages -->
|
<!-- Pakfire log messages -->
|
||||||
<pre id="pflog-messages"></pre>
|
<pre id="pflog-messages"></pre>
|
||||||
<script>
|
<script>
|
||||||
|
// Start automatic log refresh
|
||||||
pakfire.running = true;
|
pakfire.running = true;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -401,13 +414,22 @@ END
|
|||||||
|
|
||||||
# Check if pakfire is already running (extend test here if necessary)
|
# Check if pakfire is already running (extend test here if necessary)
|
||||||
sub _is_pakfire_busy {
|
sub _is_pakfire_busy {
|
||||||
# Get PID of a running pakfire instance
|
# Return immediately if lockfile is present
|
||||||
|
if(-e "$Pakfire::lockfile") {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if a PID of a running pakfire instance is found
|
||||||
# (The system backpipe command is safe, because no user input is computed.)
|
# (The system backpipe command is safe, because no user input is computed.)
|
||||||
my $pakfire_pid = `pidof -s /usr/local/bin/pakfire`;
|
my $pakfire_pid = `pidof -s /usr/local/bin/pakfire`;
|
||||||
chomp($pakfire_pid);
|
chomp($pakfire_pid);
|
||||||
|
|
||||||
# Test presence of PID or lockfile
|
if($pakfire_pid) {
|
||||||
return (($pakfire_pid) || (-e "$Pakfire::lockfile"));
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Pakfire isn't running
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
# Send HTTP headers
|
# Send HTTP headers
|
||||||
|
|||||||
@@ -32,12 +32,13 @@ class PakfireJS {
|
|||||||
this._states = Object.create(null);
|
this._states = Object.create(null);
|
||||||
this._states.running = false;
|
this._states.running = false;
|
||||||
this._states.reboot = false;
|
this._states.reboot = false;
|
||||||
|
this._states.failure = false;
|
||||||
|
|
||||||
// Status refresh helper
|
// Status refresh helper
|
||||||
this._autoRefresh = {
|
this._autoRefresh = {
|
||||||
delay: 1000, //Delay between requests (default: 1s)
|
delay: 1000, //Delay between requests (minimum: 500, default: 1s)
|
||||||
jsonAction: 'getstatus', //CGI POST action parameter
|
jsonAction: 'getstatus', //CGI POST action parameter
|
||||||
timeout: 5000, //XHR timeout (5s)
|
timeout: 5000, //XHR timeout (0 to disable, default: 5s)
|
||||||
|
|
||||||
delayTimer: null, //setTimeout reference
|
delayTimer: null, //setTimeout reference
|
||||||
jqXHR: undefined, //jQuery.ajax promise reference
|
jqXHR: undefined, //jQuery.ajax promise reference
|
||||||
@@ -51,10 +52,32 @@ class PakfireJS {
|
|||||||
return (this.runningDelay || this.runningXHR);
|
return (this.runningDelay || this.runningXHR);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Return to main screen helper
|
||||||
|
this._pageReload = {
|
||||||
|
delay: 1000, //Delay before page reload (default: 1s)
|
||||||
|
enabled: false, //Reload disabled by default
|
||||||
|
|
||||||
|
delayTimer: null, //setTimeout reference
|
||||||
|
get isTriggered() { //Reload timer started
|
||||||
|
return (this.delayTimer !== null);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
//### Public properties ###
|
//### Public properties ###
|
||||||
|
|
||||||
|
// Note on using the status flags
|
||||||
|
// running: Pakfire is performing a task.
|
||||||
|
// Writing "true" activates the periodic AJAX/JSON status polling, writing "false" stops polling.
|
||||||
|
// When the task has been completed, status polling stops and this returns to "false".
|
||||||
|
// The page can then be reloaded to go back to the main screen. Writing "false" does not trigger a reload.
|
||||||
|
// "refreshInterval" and "setupPageReload" can be used to adjust the respective behaviour.
|
||||||
|
// reboot: An update requires a reboot.
|
||||||
|
// If set to "true", a link to the reboot menu is shown after the task is completed.
|
||||||
|
// failure: An error has occured.
|
||||||
|
// To display the error log, the page does not return to the main screen.
|
||||||
|
|
||||||
// Pakfire is running (true/false)
|
// Pakfire is running (true/false)
|
||||||
set running(state) {
|
set running(state) {
|
||||||
if(this._states.running !== state) {
|
if(this._states.running !== state) {
|
||||||
@@ -77,6 +100,17 @@ class PakfireJS {
|
|||||||
return this._states.reboot;
|
return this._states.reboot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Error encountered (true/false)
|
||||||
|
set failure(state) {
|
||||||
|
if(this._states.failure !== state) {
|
||||||
|
this._states.failure = state;
|
||||||
|
this._states_onChange('failure');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
get failure() {
|
||||||
|
return this._states.failure;
|
||||||
|
}
|
||||||
|
|
||||||
// Status refresh interval in ms
|
// Status refresh interval in ms
|
||||||
set refreshInterval(delay) {
|
set refreshInterval(delay) {
|
||||||
if(delay < 500) {
|
if(delay < 500) {
|
||||||
@@ -88,6 +122,16 @@ class PakfireJS {
|
|||||||
return this._autoRefresh.delay;
|
return this._autoRefresh.delay;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Configure page reload after successful task (returns to main screen)
|
||||||
|
// delay: In ms
|
||||||
|
setupPageReload(enabled, delay) {
|
||||||
|
if(delay < 0) {
|
||||||
|
delay = 0;
|
||||||
|
}
|
||||||
|
this._pageReload.delay = delay;
|
||||||
|
this._pageReload.enabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
// Document loaded (call once from jQuery.ready)
|
// Document loaded (call once from jQuery.ready)
|
||||||
documentReady() {
|
documentReady() {
|
||||||
// Status refresh late start
|
// Status refresh late start
|
||||||
@@ -96,6 +140,12 @@ class PakfireJS {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reload entire CGI page (clears POST/GET data from history)
|
||||||
|
documentReload() {
|
||||||
|
let url = window.location.origin + window.location.pathname;
|
||||||
|
window.location.replace(url);
|
||||||
|
}
|
||||||
|
|
||||||
//### Private properties ###
|
//### Private properties ###
|
||||||
|
|
||||||
// Pakfire status change handler
|
// Pakfire status change handler
|
||||||
@@ -106,9 +156,13 @@ class PakfireJS {
|
|||||||
$('#pflog-status').text(this.i18n.get('working'));
|
$('#pflog-status').text(this.i18n.get('working'));
|
||||||
$('#pflog-action').empty();
|
$('#pflog-action').empty();
|
||||||
} else {
|
} else {
|
||||||
$('#pflog-status').text(this.i18n.get('finished'));
|
if(this.failure) {
|
||||||
|
$('#pflog-status').text(this.i18n.get('finished error'));
|
||||||
|
} else {
|
||||||
|
$('#pflog-status').text(this.i18n.get('finished'));
|
||||||
|
}
|
||||||
if(this.reboot) { //Enable return or reboot links in UI
|
if(this.reboot) { //Enable return or reboot links in UI
|
||||||
$('#pflog-action').html(this.i18n.get('link_reboot'));
|
$('#pflog-action').html(this.i18n.get('link_return') + " • " + this.i18n.get('link_reboot'));
|
||||||
} else {
|
} else {
|
||||||
$('#pflog-action').html(this.i18n.get('link_return'));
|
$('#pflog-action').html(this.i18n.get('link_return'));
|
||||||
}
|
}
|
||||||
@@ -122,6 +176,13 @@ class PakfireJS {
|
|||||||
this._autoRefresh_clearSchedule();
|
this._autoRefresh_clearSchedule();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Always stay in the log viewer if Pakfire failed
|
||||||
|
if(property === 'failure') {
|
||||||
|
if(this.failure) {
|
||||||
|
this._pageReload_cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//--- Status refresh scheduling functions ---
|
//--- Status refresh scheduling functions ---
|
||||||
@@ -164,6 +225,25 @@ class PakfireJS {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start delayed page reload to return to main screen
|
||||||
|
_pageReload_trigger() {
|
||||||
|
if((! this._pageReload.enabled) || this._pageReload.isTriggered) {
|
||||||
|
return; // Disabled or already started
|
||||||
|
}
|
||||||
|
this._pageReload.delayTimer = window.setTimeout(function() {
|
||||||
|
this._pageReload.delayTimer = null;
|
||||||
|
this.documentReload();
|
||||||
|
}.bind(this), this._pageReload.delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop scheduled reload
|
||||||
|
_pageReload_cancel() {
|
||||||
|
if(this._pageReload.isTriggered) {
|
||||||
|
window.clearTimeout(this._pageReload.delayTimer);
|
||||||
|
this._pageReload.delayTimer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//--- JSON request & data handling ---
|
//--- JSON request & data handling ---
|
||||||
|
|
||||||
// Load JSON data from Pakfire CGI, using a POST request
|
// Load JSON data from Pakfire CGI, using a POST request
|
||||||
@@ -192,10 +272,11 @@ class PakfireJS {
|
|||||||
// Update status flags
|
// Update status flags
|
||||||
this.running = (data['running'] != '0');
|
this.running = (data['running'] != '0');
|
||||||
this.reboot = (data['reboot'] != '0');
|
this.reboot = (data['reboot'] != '0');
|
||||||
|
this.failure = (data['failure'] != '0');
|
||||||
|
|
||||||
// Update timer display
|
// Update timer display
|
||||||
if(this.running && data['running_since']) {
|
if(this.running && data['running_since']) {
|
||||||
$('#pflog-time').text(this.i18n.get('since') + data['running_since']);
|
$('#pflog-time').text(this.i18n.get('since') + " " + data['running_since']);
|
||||||
} else {
|
} else {
|
||||||
$('#pflog-time').empty();
|
$('#pflog-time').empty();
|
||||||
}
|
}
|
||||||
@@ -206,6 +287,11 @@ class PakfireJS {
|
|||||||
messages += `${line}\n`;
|
messages += `${line}\n`;
|
||||||
});
|
});
|
||||||
$('#pflog-messages').text(messages);
|
$('#pflog-messages').text(messages);
|
||||||
|
|
||||||
|
// Pakfire finished without errors, return to main screen
|
||||||
|
if((! this.running) && (! this.failure)) {
|
||||||
|
this._pageReload_trigger();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1990,7 +1990,8 @@
|
|||||||
'pakfire configuration' => 'Pakfire Konfiguration',
|
'pakfire configuration' => 'Pakfire Konfiguration',
|
||||||
'pakfire core update auto' => 'Core- und Addon-Updates automatisch installieren:',
|
'pakfire core update auto' => 'Core- und Addon-Updates automatisch installieren:',
|
||||||
'pakfire core update level' => 'Core-Update-Level',
|
'pakfire core update level' => 'Core-Update-Level',
|
||||||
'pakfire finished' => 'Pakfire ist fertig!. Bitte überprüfen Sie die Log Ausgabe.',
|
'pakfire finished' => 'Pakfire ist fertig! Kehre zurück...',
|
||||||
|
'pakfire finished error' => 'Pakfire ist fertig! Fehler sind aufgetreten, bitte überprüfen Sie die Log-Ausgabe, bevor Sie fortfahren.',
|
||||||
'pakfire health check' => 'Mirrors auf Erreichbarkeit prüfen (Ping):',
|
'pakfire health check' => 'Mirrors auf Erreichbarkeit prüfen (Ping):',
|
||||||
'pakfire install description' => 'Wählen Sie ein oder mehrere Pakete zur Installation aus und drücken Sie auf das plus-Symbol.',
|
'pakfire install description' => 'Wählen Sie ein oder mehrere Pakete zur Installation aus und drücken Sie auf das plus-Symbol.',
|
||||||
'pakfire install package' => 'Sie möchten folgende Pakete installieren: ',
|
'pakfire install package' => 'Sie möchten folgende Pakete installieren: ',
|
||||||
|
|||||||
@@ -2025,7 +2025,8 @@
|
|||||||
'pakfire configuration' => 'Pakfire Configuration',
|
'pakfire configuration' => 'Pakfire Configuration',
|
||||||
'pakfire core update auto' => 'Install core and addon updates automatically:',
|
'pakfire core update auto' => 'Install core and addon updates automatically:',
|
||||||
'pakfire core update level' => 'Core-Update-Level',
|
'pakfire core update level' => 'Core-Update-Level',
|
||||||
'pakfire finished' => 'Pakfire is finished! Please check the log output.',
|
'pakfire finished' => 'Pakfire has finished! Returning...',
|
||||||
|
'pakfire finished error' => 'Pakfire has finished! Errors occurred, please check the log output before proceeding.',
|
||||||
'pakfire health check' => 'Check if mirror is reachable (ping):',
|
'pakfire health check' => 'Check if mirror is reachable (ping):',
|
||||||
'pakfire install description' => 'Please choose one or more items from the list below and click the plus to install.',
|
'pakfire install description' => 'Please choose one or more items from the list below and click the plus to install.',
|
||||||
'pakfire install package' => 'You want to install the following packages: ',
|
'pakfire install package' => 'You want to install the following packages: ',
|
||||||
|
|||||||
Reference in New Issue
Block a user