From: Stefan Schantl Date: Thu, 3 Jul 2025 16:51:42 +0000 (+0200) Subject: Introduce snapshots.cgi page X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=104a193d54b4d7f76bf58f9bbe3a8b57ec2292e1;p=people%2Fstevee%2Fipfire-2.x.git Introduce snapshots.cgi page This new CGI page can be used to create and deal with BTRFS snapshots. The access to this page will be in the "System" menu in case you have chossesn BTRFS during setup. Signed-off-by: Stefan Schantl --- diff --git a/config/menu/10-system.menu b/config/menu/10-system.menu index b142bfbac..9c37591cd 100644 --- a/config/menu/10-system.menu +++ b/config/menu/10-system.menu @@ -33,25 +33,31 @@ 'title' => "$Lang::tr{'gui settings'}", 'enabled' => 1, }; - $subsystem->{'40.backup'} = { + $subsystem->{'50.backup'} = { 'caption' => $Lang::tr{'backup'}, 'uri' => '/cgi-bin/backup.cgi', 'title' => "$Lang::tr{'backup'}", 'enabled' => 1, }; - $subsystem->{'41.fireinfo'} = { + $subsystem->{'60.snapshots'} = { + 'caption' => $Lang::tr{'snapshots'}, + 'uri' => '/cgi-bin/snapshots.cgi', + 'title' => "$Lang::tr{'snapshots'}" + 'enabled' => (`stat -f --format="%T" / | grep btrfs` ? 1 : 0), + }; + $subsystem->{'70.fireinfo'} = { 'caption' => $Lang::tr{'system information'}, 'uri' => '/cgi-bin/fireinfo.cgi', 'title' => "$Lang::tr{'system information'}", 'enabled' => 1, }; - $subsystem->{'42.hwvuln'} = { + $subsystem->{'80.hwvuln'} = { 'caption' => $Lang::tr{'hardware vulnerabilities'}, 'uri' => '/cgi-bin/vulnerabilities.cgi', 'title' => "$Lang::tr{'hardware vulnerabilities'}", 'enabled' => 1, }; - $subsystem->{'43.shutdown'} = { + $subsystem->{'90.shutdown'} = { 'caption' => $Lang::tr{'shutdown'}, 'uri' => '/cgi-bin/shutdown.cgi', 'title' => "$Lang::tr{'shutdown'}", diff --git a/config/rootfiles/common/web-user-interface b/config/rootfiles/common/web-user-interface index aa31491d2..941199c81 100644 --- a/config/rootfiles/common/web-user-interface +++ b/config/rootfiles/common/web-user-interface @@ -71,6 +71,7 @@ srv/web/ipfire/cgi-bin/remote.cgi srv/web/ipfire/cgi-bin/routing.cgi #srv/web/ipfire/cgi-bin/samba.cgi srv/web/ipfire/cgi-bin/services.cgi +srv/web/ipfire/cgi-bin/snapshots.cgi srv/web/ipfire/cgi-bin/shutdown.cgi srv/web/ipfire/cgi-bin/speed.cgi srv/web/ipfire/cgi-bin/system.cgi diff --git a/html/cgi-bin/snapshots.cgi b/html/cgi-bin/snapshots.cgi new file mode 100644 index 000000000..029682f1e --- /dev/null +++ b/html/cgi-bin/snapshots.cgi @@ -0,0 +1,223 @@ +#!/usr/bin/perl +############################################################################### +# # +# IPFire.org - A linux based firewall # +# Copyright (C) 2007-2025 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 # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see . # +# # +############################################################################### + +use strict; +# enable only the following on debugging purpose +#use warnings; +#use CGI::Carp 'fatalsToBrowser'; + +# Load function from posix module to format time strings. +use POSIX qw (strftime); + +# Load module to call stat on files/directories. +use File::stat; + +require '/var/ipfire/general-functions.pl'; +require "${General::swroot}/filesystem-functions.pl"; +require "${General::swroot}/lang.pl"; +require "${General::swroot}/header.pl"; + +my $error; + +my %color = (); +my %mainsettings = (); +my %settings = (); +my %cgiparams = (); + +&General::readhash("${General::swroot}/main/settings", \%mainsettings); +&General::readhash("/srv/web/ipfire/html/themes/ipfire/include/colors.txt", \%color); + +&Header::showhttpheaders(); + +# Get GUI values +&Header::getcgihash(\%cgiparams); + +# Get all available snapshots in case root is running as BTRFS. +my %snapshots = &Filesystem::btrfs_get_snapshots() if &Filesystem::is_btrfs("/"); + +# If any kind of id is given, check if it is valid/known. +if (exists($cgiparams{'ID'}) and not exists($snapshots{$cgiparams{'ID'}})) { + $error = "$Lang::tr{'snapshot error unknown id'}"; + + # Reset the cgiparams + %cgiparams; +} + +if ($cgiparams{'SNAPSHOT'} eq "create") { + # Reverse the snapshots hash and store in new hash. + my %known_snapshot_names = reverse(%snapshots); + + # Store the given snapshot name in a nice acessible variable. + my $snapshot_name = $cgiparams{'SNAPSHOT_NAME'} if ($cgiparams{'SNAPSHOT_NAME'}); + + # Abort if the filesystem does not contain a btrfs. + if ( ! &Filesystem::is_btrfs("/")) { + $error = "$Lang::tr{'snapshot error no btrfs'}"; + } + + # Check for empty input. + elsif ( ! $snapshot_name) { + $error = "$Lang::tr{'snapshot error empty input'}"; + } + + # Check allowed characters. + elsif ($snapshot_name !~ /^[A-Za-z0-9_\-]+$/) { + $error = "$Lang::tr{'snapshot error unallowed chars'}"; + } + + # Check, if the give snapshot name allready exists. + elsif (exists($known_snapshot_names{$snapshot_name})) { + $error = "$Lang::tr{'snapshot error snapshot exists'}"; + } + + # Go furter if no error occurs. + unless ($error) { + # Create the snapshot. + my $ret = &Filesystem::btrfs_create_snapshot($snapshot_name); + + # Return error if the snapshot could not be created. + $error = "ERROR: $ret" if ($ret); + } + +} elsif ($cgiparams{'SNAPSHOT'} eq "boot") { + # Call function and boot into the the given snapshot. + my $ret = &Filesystem::btrfs_restore_snapshot($cgiparams{'ID'}); + + # Return error if the snapshot can not be used to boot. + $error = "ERROR: $ret" if ($ret); + + +} elsif ($cgiparams{'SNAPSHOT'} eq "restore") { + # Call function to restore the selected snapshot. + my $ret = &Filesystem::btrfs_restore_snapshot($cgiparams{'ID'}); + + # Return error if the snapshot can not be restored. + $error = "ERROR: $ret" if ($ret); + +} elsif ($cgiparams{'SNAPSHOT'} eq "delete") { + # Call function and delete the snapshot. + my $ret = &Filesystem::btrfs_remove_snapshot($cgiparams{'ID'}); + + # Return error if the snapshot could not be deleted. + $error = "Error: $ret" if ($ret); +} + +# Reload known snapshots if neccessary. +%snapshots = &Filesystem::btrfs_get_snapshots() if ( exists($cgiparams{'SNAPSHOT'}) and &Filesystem::is_btrfs("/")); + +&Header::openpage($Lang::tr{'snapshots'}, 1, ''); + +# Display error message, in case there is one. +if ($error) { + &Header::openbox('100%', 'left', $Lang::tr{'error messages'}); + print "$error\n"; + print " \n"; + &Header::closebox(); +} + +print"
\n"; + +&Header::openbox('100%', 'center', $Lang::tr{'snapshot create'}); +print < + + + $Lang::tr{'name'}:   + + + + + + + + +END + +&Header::closebox(); + +&Header::openbox('100%', 'center', $Lang::tr{'snapshots available'}); + +print"\n"; + +my $lines; +my $col; +foreach my $snapshot_id (sort { $b <=> $a } keys %snapshots) { + # Get snapshot name. + my $snapshot_name = $snapshots{$snapshot_id}; + + # Generate full snapshot directory. + my $snapshot_dir = "$Filesystem::btrfs_snapshot_dir" . "$snapshot_name"; + + # Use stat to get the creation date of the snapshot. + my $stats = stat($snapshot_dir) if (-e $snapshot_dir); + + # Convert timestamp into human-readable format. + my $snapshot_date = strftime('%Y-%m-%d %H:%M:%S', localtime($stats->mtime)); + + # Colour lines. + if ($lines % 2) { + $col="bgcolor='$color{'color20'}'"; + } else { + $col="bgcolor='$color{'color22'}'"; + } + + print"\n"; + print "\n"; + print "\n"; + + print "\n"; + + print "\n"; + + print "\n"; + print "\n"; + +$lines++; +} + +print < + +END + +&Header::closebox(); + print "\n"; + +&Header::closebigbox(); +&Header::closepage(); diff --git a/langs/de/cgi-bin/de.pl b/langs/de/cgi-bin/de.pl index 4df95fdaf..4094b96e2 100644 --- a/langs/de/cgi-bin/de.pl +++ b/langs/de/cgi-bin/de.pl @@ -2316,6 +2316,17 @@ 'smt not supported' => 'Simultanes Multi-Threading nicht unterstützt', 'smtphost' => 'Smtp Host', 'smtpport' => 'Smtp Port', +'snapshot boot into' => 'Neustarten und in den Snapshot booten', +'snapshot create' => 'Neuen Snapshot erstellen', +'snapshot delete' => 'Snapshot löschen', +'snapshot error empty input' => 'Kein Snapshot Name angegeben.', +'snapshot error no btrfs' => 'Der Datenträger enthält kein gültiges BTRFS Dateisystem.', +'snapshot error snapshot exists' => 'Ein Snapshot mit diesem Namen existiert bereits.', +'snapshot error unallowed chars' => 'Unerlaubte Zeichen verwendet - Erlaubt sind Buchstaben, Nummern, Bindestriche und Unterstriche.', +'snapshot error unknown id' => 'Diese Snapshot ID existiert nicht.', +'snapshot restore' => 'Neustarten und den Snapshot wiederherstellen', +'snapshots' => 'Snapshots', +'snapshots available' => 'Verfügbare Snapshots', 'snat new source ip address' => 'Neue Quell-IP-Adresse', 'socket options' => 'Socket Options', 'software version' => 'Software-Version', diff --git a/langs/en/cgi-bin/en.pl b/langs/en/cgi-bin/en.pl index 647fcc8da..e66a5e6e4 100644 --- a/langs/en/cgi-bin/en.pl +++ b/langs/en/cgi-bin/en.pl @@ -2397,6 +2397,17 @@ 'smt not supported' => 'Simultaneous Multi-Threading (SMT) is not supported', 'smtphost' => 'SMTP host', 'smtpport' => 'SMTP port', +'snapshot boot into' => 'Restart and boot into snapshot', +'snapshot create' => 'Create a new snapshot', +'snapshot delete' => 'Delete snapshot', +'snapshot error empty input' => 'Empty snapshot name.', +'snapshot error no btrfs' => 'The filesystem does not contain a valid BTRFS.', +'snapshot error snapshot exists' => 'A snapshot with this name allready exists.', +'snapshot error unallowed chars' => 'Unallowed characters used - Allowed are letters, numbers, minus and underscores.', +'snapshot error unknown id' => 'The given snapshot id does not exist.', +'snapshot restore' => 'Restart and restore snapshot', +'snapshots' => 'Snapshots', +'snapshots available' => 'Available snapshots', 'snat new source ip address' => 'New source IP address', 'socket options' => 'Socket options', 'software version' => 'Software Version',
$snapshot_date$snapshot_name\n"; + print "
\n"; + print "\n"; + print "\n"; + print "\n"; + print "
\n"; + print "
\n"; + print "
\n"; + print "\n"; + print "\n"; + print "\n"; + print "
\n"; + print "
\n"; + print "
\n"; + print "\n"; + print "\n"; + print "\n"; + print "
\n"; + print "