X-Git-Url: http://git.ipfire.org/?p=people%2Fpmueller%2Fipfire-2.x.git;a=blobdiff_plain;f=html%2Fcgi-bin%2Fpakfire.cgi;h=0cf522ba13675cee3f4407f7b9a51f2e9ba9476a;hp=f2381f03126b0463ea0ad86affe8847aab5838bd;hb=HEAD;hpb=90d8cb92980bb6ae689605ad36d6f0c46158be7c diff --git a/html/cgi-bin/pakfire.cgi b/html/cgi-bin/pakfire.cgi index f2381f0312..42c603c613 100644 --- a/html/cgi-bin/pakfire.cgi +++ b/html/cgi-bin/pakfire.cgi @@ -2,7 +2,7 @@ ############################################################################### # # # IPFire.org - A linux based firewall # -# Copyright (C) 2007-2011 Michael Tremer & Christian Schmidt # +# Copyright (C) 2007-2022 IPFire Team # # # # This program is free software: you can redistribute it and/or modify # # it under the terms of the GNU General Public License as published by # @@ -20,6 +20,8 @@ ############################################################################### use strict; +use List::Util qw(any); +use URI; # enable only the following on debugging purpose #use warnings; @@ -36,287 +38,422 @@ my %color = (); my %pakfiresettings = (); my %mainsettings = (); -&Header::showhttpheaders(); +# The page mode is used to explictly switch between user interface functions: +my $PM_DEFAULT = 'default'; # Default user interface with command processing +my $PM_LOGREAD = 'logread'; # Log messages viewer (ignores all commands) +my $pagemode = $PM_DEFAULT; + +# Get Pakfire status +my %pakfire_status = &Pakfire::status(); +# Load general settings +&General::readhash("${General::swroot}/main/settings", \%mainsettings); +&General::readhash("${General::swroot}/pakfire/settings", \%pakfiresettings); +&General::readhash("/srv/web/ipfire/html/themes/ipfire/include/colors.txt", \%color); + +# Get CGI POST request data $cgiparams{'ACTION'} = ''; -$cgiparams{'VALID'} = ''; +$cgiparams{'FORCE'} = ''; $cgiparams{'INSPAKS'} = ''; $cgiparams{'DELPAKS'} = ''; -my $page_lock; +&Header::getcgihash(\%cgiparams); -sub refreshpage{&Header::openbox( 'Waiting', 1, "" );print "

$Lang::tr{'pagerefresh'}
";&Header::closebox();} +# Get CGI GET request data (if available) +if($ENV{'QUERY_STRING'}) { + my $uri = URI->new($ENV{'REQUEST_URI'}); + my %query = $uri->query_form; -&Header::getcgihash(\%cgiparams); + my $mode = lc($query{'mode'} // ''); + if(($mode eq $PM_DEFAULT) || ($mode eq $PM_LOGREAD)) { + $pagemode = $mode; # Limit to existing modes + } +} -&General::readhash("${General::swroot}/main/settings", \%mainsettings); -&General::readhash("/srv/web/ipfire/html/themes/ipfire/include/colors.txt", \%color); +### Process AJAX/JSON request ### +if($cgiparams{'ACTION'} eq 'json-getstatus') { + # Send HTTP headers + _start_json_output(); -&Header::openpage($Lang::tr{'pakfire configuration'}, 1); -&Header::openbigbox('100%', 'left', '', $errormessage); + # 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 2>/dev/null | sed -n '/pakfire:/{p;/Pakfire.*started/q}'`; -if (($cgiparams{'ACTION'} eq 'install') && (! -e $Pakfire::lockfile)) { - my @pkgs = split(/\|/, $cgiparams{'INSPAKS'}); - if ("$cgiparams{'FORCE'}" eq "on") { - # Lock the page. - $page_lock = "1"; - &General::system_background("/usr/local/bin/pakfire", "install", "--non-interactive", "--no-colors", @pkgs); - } else { - &Header::openbox("100%", "center", $Lang::tr{'request'}); - my @output = &General::system_output("/usr/local/bin/pakfire", "resolvedeps", "--no-colors", @pkgs); - print <$Lang::tr{'pakfire install package'} @pkgs $Lang::tr{'pakfire possible dependency'} -
-END
-		foreach (@output) {
-		  $_ =~ s/\\[[0-1]\;[0-9]+m//g;
-			print "$_\n";
-		}
-		print <
-		$Lang::tr{'pakfire accept all'}
-		 
-		
- - - - -
- -
- - -
- -END - &Header::closebox(); - &Header::closebigbox(); - &Header::closepage(); - exit; + # 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 = ( + 'running' => &_is_pakfire_busy() || "0", + 'running_since' => &General::age("$Pakfire::lockfile") || "0s", + 'reboot' => ("$pakfire_status{'RebootRequired'}" eq "yes") || "0", + 'failure' => $failure || "0" + ); + + # Start JSON file + print "{\n"; + + foreach my $key (keys %status) { + my $value = $status{$key}; + print qq{\t"$key": "$value",\n}; } -} elsif (($cgiparams{'ACTION'} eq 'remove') && (! -e $Pakfire::lockfile)) { - my @pkgs = split(/\|/, $cgiparams{'DELPAKS'}); - if ("$cgiparams{'FORCE'}" eq "on") { - # Lock the page. - $page_lock = "1"; + + # Print sanitized messages in reverse order to undo previous "tac" + print qq{\t"messages": [\n}; + for my $index (reverse (0 .. $#messages)) { + my $line = $messages[$index]; + $line =~ s/[[:cntrl:]<>&\\]+//g; + + print qq{\t\t"$line"}; + print ",\n" unless $index < 1; + } + print "\n\t]\n"; + + # Finalize JSON file & stop + print "}"; + exit; +} + +### Process Pakfire install/update commands ### +if(($cgiparams{'ACTION'} ne '') && ($pagemode eq $PM_DEFAULT)) { + if(&_is_pakfire_busy()) { + $errormessage = $Lang::tr{'pakfire already busy'}; + $pagemode = $PM_LOGREAD; # Running Pakfire instance found, switch to log viewer mode + } elsif(($cgiparams{'ACTION'} eq 'install') && ($cgiparams{'FORCE'} eq 'on')) { + my @pkgs = split(/\|/, $cgiparams{'INSPAKS'}); + &General::system_background("/usr/local/bin/pakfire", "install", "--non-interactive", "--no-colors", @pkgs); + &_http_pagemode_redirect($PM_LOGREAD, 1); + } elsif(($cgiparams{'ACTION'} eq 'remove') && ($cgiparams{'FORCE'} eq 'on')) { + my @pkgs = split(/\|/, $cgiparams{'DELPAKS'}); &General::system_background("/usr/local/bin/pakfire", "remove", "--non-interactive", "--no-colors", @pkgs); - } else { - &Header::openbox("100%", "center", $Lang::tr{'request'}); - my @output = &General::system_output("/usr/local/bin/pakfire", "resolvedeps", "--no-colors", @pkgs); - print <$Lang::tr{'pakfire uninstall package'} @pkgs $Lang::tr{'pakfire possible dependency'} -
-END
-		foreach (@output) {
-		  $_ =~ s/\\[[0-1]\;[0-9]+m//g;
-			print "$_\n";
+		&_http_pagemode_redirect($PM_LOGREAD, 1);
+	} elsif($cgiparams{'ACTION'} eq 'update') {
+		&General::system_background("/usr/local/bin/pakfire", "update", "--force", "--no-colors");
+		&_http_pagemode_redirect($PM_LOGREAD, 1);
+	} elsif($cgiparams{'ACTION'} eq 'upgrade') {
+		&General::system_background("/usr/local/bin/pakfire", "upgrade", "-y", "--no-colors");
+		&_http_pagemode_redirect($PM_LOGREAD, 1);
+	} elsif($cgiparams{'ACTION'} eq $Lang::tr{'save'}) {
+		$pakfiresettings{"TREE"} = $cgiparams{"TREE"};
+
+		# Check for valid input
+		if ($pakfiresettings{"TREE"} !~ m/^(stable|testing|unstable)$/) {
+			$errormessage .= $Lang::tr{'pakfire invalid tree'};
+		}
+
+		unless ($errormessage) {
+			&General::writehash("${General::swroot}/pakfire/settings", \%pakfiresettings);
+
+			# Update lists
+			&General::system_background("/usr/local/bin/pakfire", "update", "--force", "--no-colors");
+			&_http_pagemode_redirect($PM_LOGREAD, 1);
 		}
-		print <
-		$Lang::tr{'pakfire uninstall all'}
-		 
-		
- - - - -
- -
- - -
- -END - &Header::closebox(); - &Header::closebigbox(); - &Header::closepage(); - exit; } +} + +### Start pakfire page ### +&Header::showhttpheaders(); -} elsif (($cgiparams{'ACTION'} eq 'update') && (! -e $Pakfire::lockfile)) { - # Set variable to lock the page. - $page_lock = "1"; - &General::system_background("/usr/local/bin/pakfire", "update", "--force", "--no-colors"); -} elsif (($cgiparams{'ACTION'} eq 'upgrade') && (!-e $Pakfire::lockfile)) { - # Lock the page. - $page_lock = "1"; - &General::system_background("/usr/local/bin/pakfire", "upgrade", "-y", "--no-colors"); -} elsif ($cgiparams{'ACTION'} eq "$Lang::tr{'save'}") { - $pakfiresettings{"TREE"} = $cgiparams{"TREE"}; - - # Check for valid input - if ($pakfiresettings{"TREE"} !~ m/^(stable|testing|unstable)$/) { - $errormessage .= $Lang::tr{'pakfire invalid tree'}; +###--- HTML HEAD ---### +my $extraHead = < + /* Main screen */ + table#pfmain { + width: 100%; + border-style: hidden; + table-layout: fixed; } - unless ($errormessage) { - &General::writehash("${General::swroot}/pakfire/settings", \%pakfiresettings); + #pfmain td { + padding: 5px 20px 0; + text-align: center; + } + #pfmain tr:not(:last-child) > td { + padding-bottom: 1.5em; + } + #pfmain tr > td.heading { + padding: 0; + font-weight: bold; + background-color: $color{'color20'}; + } - # Update lists - &General::system_background("/usr/local/bin/pakfire", "update", "--force", "--no-colors"); + .pflist { + width: 100%; + text-align: left; + margin-bottom: 0.8em; } -} -&General::readhash("${General::swroot}/pakfire/settings", \%pakfiresettings); + /* Pakfire log viewer */ + section#pflog-header { + width: 100%; + display: flex; + text-align: left; + align-items: center; + column-gap: 20px; + } + #pflog-header > div:last-child { + margin-left: auto; + margin-right: 20px; + } + #pflog-header span { + line-height: 1.3em; + } + #pflog-header span:empty::before { + content: "\\200b"; /* zero width space */ + } -my %selected=(); -my %checked=(); + pre#pflog-messages { + margin-top: 0.7em; + padding-top: 0.7em; + border-top: 0.5px solid $Header::bordercolour; -$selected{"TREE"} = (); -$selected{"TREE"}{"stable"} = ""; -$selected{"TREE"}{"testing"} = ""; -$selected{"TREE"}{"unstable"} = ""; -$selected{"TREE"}{$pakfiresettings{"TREE"}} = "selected"; + text-align: left; + min-height: 15em; + overflow-x: auto; + } + + + + +END +; +###--- END HTML HEAD ---### + +&Header::openpage($Lang::tr{'pakfire configuration'}, 1, $extraHead); +&Header::openbigbox('100%', 'left', '', $errormessage); -# DPC move error message to top so it is seen! +# Show error message if ($errormessage) { &Header::openbox('100%', 'left', $Lang::tr{'error messages'}); print "$errormessage \n"; &Header::closebox(); } -# Check if a page lock is required. -if ($page_lock) { - &Header::openbox('Waiting', 1, ,); - print < - - - $Lang::tr{ - +# Show only log output while Pakfire is running and stop afterwards +if(($pagemode eq $PM_LOGREAD) || (&_is_pakfire_busy())) { + &Header::openbox("100%", "center", "Pakfire"); + + print < +
$Lang::tr{'active'}
+
+ $Lang::tr{'pakfire working'}
+
+ +
+
$Lang::tr{'refresh'}
+ + + +

+
 
-				
-					$Lang::tr{'pakfire working'}
-				
-			
-		
 END
+;
+
 	&Header::closebox();
+	&Header::closebigbox();
+	&Header::closepage();
+	exit;
+}
 
-	# Infinite loop to lock the page until pakfire lockfile is present.
-	while($page_lock) {
-		unless (-e $Pakfire::lockfile) {
-			sleep(1);
-		} else {
-			# Release page lock.
-			undef($page_lock);
+# Show Pakfire install/remove dependencies and confirm form
+# (_is_pakfire_busy status was checked before and can be omitted)
+if (($cgiparams{'ACTION'} eq 'install') && ($pagemode eq $PM_DEFAULT)) {
+	&Header::openbox("100%", "center", $Lang::tr{'request'});
 
-			# Break loop.
-			last;
-		}
+	my @pkgs = split(/\|/, $cgiparams{'INSPAKS'});
+	my @output = &General::system_output("/usr/local/bin/pakfire", "resolvedeps", "--no-colors", @pkgs);
+	print <

$Lang::tr{'pakfire install package'} @{pkgs}
+ $Lang::tr{'pakfire possible dependency'}

+
+END
+	foreach (@output) {
+		$_ =~ s/\\[[0-1]\;[0-9]+m//g;
+		print "$_\n";
 	}
-
-	# Perform page reload.
-	print "\n";
+	print <
+		$Lang::tr{'pakfire accept all'}
+		 
+		
+ + + + +
+ + +
+ + +
+ + + +END + &Header::closebox(); + &Header::closebigbox(); + &Header::closepage(); exit; -} -# Check if pakfire is already running. In this case a lockfile is present. -if (-e $Pakfire::lockfile) { - &Header::openbox( 'Waiting', 1, "" ); +} elsif (($cgiparams{'ACTION'} eq 'remove') && ($pagemode eq $PM_DEFAULT)) { + &Header::openbox("100%", "center", $Lang::tr{'request'}); + + my @pkgs = split(/\|/, $cgiparams{'DELPAKS'}); + my @output = &General::system_output("/usr/local/bin/pakfire", "resolvedeps", "--no-colors", @pkgs); print < - - $Lang::tr{  - - $Lang::tr{'pakfire working'} - -
- -
- +

$Lang::tr{'pakfire uninstall package'} @{pkgs}
+ $Lang::tr{'pakfire possible dependency'}

+
 END
-	my @output = `grep pakfire /var/log/messages | tail -20`;
 	foreach (@output) {
-		print "$_
"; + $_ =~ s/\\[[0-1]\;[0-9]+m//g; + print "$_\n"; } print < -
+
+ $Lang::tr{'pakfire uninstall all'} +   +
+ + + + +
+ + +
+ + +
+ + + END &Header::closebox(); &Header::closebigbox(); &Header::closepage(); exit; - refreshpage(); } -my $core_release = `cat /opt/pakfire/db/core/mine 2>/dev/null`; -chomp($core_release); -my $core_update_age = &General::age("/opt/pakfire/db/core/mine"); -my $corelist_update_age = &General::age("/opt/pakfire/db/lists/core-list.db"); -my $server_update_age = &General::age("/opt/pakfire/db/lists/server-list.db"); -my $packages_update_age = &General::age("/opt/pakfire/db/lists/packages_list.db"); +# Show Pakfire main page +my %selected=(); +my %checked=(); + +$selected{"TREE"} = (); +$selected{"TREE"}{"stable"} = ""; +$selected{"TREE"}{"testing"} = ""; +$selected{"TREE"}{"unstable"} = ""; +$selected{"TREE"}{$pakfiresettings{"TREE"}} = "selected"; &Header::openbox("100%", "center", "Pakfire"); print < + END -if ( -e "/var/run/need_reboot") { - print ""; - print "" +if ("$pakfire_status{'RebootRequired'}" eq "yes") { + print "\t\t\n"; } + print < - - + + + + + + + - + +
$Lang::tr{'needreboot'}!
 
$Lang::tr{'needreboot'}!
$Lang::tr{'pakfire system state'}: - - $Lang::tr{'available updates'}:
$Lang::tr{'pakfire core update level'}: $core_release
- $Lang::tr{'pakfire last update'} $core_update_age $Lang::tr{'pakfire ago'}
- $Lang::tr{'pakfire last serverlist update'} $server_update_age $Lang::tr{'pakfire ago'}
- $Lang::tr{'pakfire last core list update'} $corelist_update_age $Lang::tr{'pakfire ago'}
- $Lang::tr{'pakfire last package update'} $packages_update_age $Lang::tr{'pakfire ago'} -
-
-
-
-
-
+
$Lang::tr{'pakfire system state'}:$Lang::tr{'available updates'}:
$Lang::tr{'pakfire core update level'}: $pakfire_status{'Release'} +
+
+ $Lang::tr{'pakfire last update'} $pakfire_status{'LastUpdate'} $Lang::tr{'pakfire ago'}
+ $Lang::tr{'pakfire last serverlist update'} $pakfire_status{'LastServerListUpdate'} $Lang::tr{'pakfire ago'}
+ $Lang::tr{'pakfire last core list update'} $pakfire_status{'LastCoreListUpdate'} $Lang::tr{'pakfire ago'}
+ $Lang::tr{'pakfire last package update'} $pakfire_status{'LastPakListUpdate'} $Lang::tr{'pakfire ago'} +
+
+ + +
+
- END - &Pakfire::dblist("upgrade", "forweb"); + + if ("$pakfire_status{'CoreUpdateAvailable'}" eq "yes") { + print "\n"; + } + + if ($pakfire_status{'PakUpdatesAvailable'} > 0) { + my %upgradelist = &Pakfire::dblist("upgrade"); + foreach my $pak (sort keys %upgradelist) { + print "\n"; + } + } + print < -
+
$Lang::tr{'pakfire available addons'}$Lang::tr{'pakfire installed addons'}
  -
$Lang::tr{'pakfire available addons'} - $Lang::tr{'pakfire installed addons'} -
-

$Lang::tr{'pakfire install description'}

-
-

$Lang::tr{'pakfire install description'}

+ + - - - -
-

$Lang::tr{'pakfire uninstall description'}

-
- + +
+

$Lang::tr{'pakfire uninstall description'}

+
+ - -
+ print < + + + +
END @@ -351,3 +488,52 @@ END &Header::closebox(); &Header::closebigbox(); &Header::closepage(); + +###--- Internal functions ---### + +# Check if pakfire is already running (extend test here if necessary) +sub _is_pakfire_busy { + # 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.) + my $pakfire_pid = `pidof -s /usr/local/bin/pakfire`; + chomp($pakfire_pid); + + if($pakfire_pid) { + return 1; + } + + # Pakfire isn't running + return 0; +} + +# Send HTTP headers +sub _start_json_output { + print "Cache-Control: no-cache, no-store\n"; + print "Content-Type: application/json\n"; + print "\n"; # End of HTTP headers +} + +# Send HTTP 303 redirect headers to change page mode +# GET is always used to display the redirected page, which will remove already processed POST form data. +# Note: Custom headers must be sent before the HTML output is started by &Header::showhttpheaders(). +# If switch_mode is set to true, the global page mode variable ("$pagemode") is also updated immediately. +sub _http_pagemode_redirect { + my ($mode, $switch_mode) = @_; + $mode //= $PM_DEFAULT; + $switch_mode //= 0; + + # Send HTTP redirect with GET parameter + my $location = "https://$ENV{'SERVER_NAME'}:$ENV{'SERVER_PORT'}$ENV{'SCRIPT_NAME'}?mode=${mode}"; + print "Status: 303 See Other\n"; + print "Location: $location\n"; + + # Change global page mode + if($switch_mode) { + $pagemode = $mode; + } +}