From: Stefan Schantl Date: Fri, 1 Aug 2025 17:45:48 +0000 (+0200) Subject: media.cgi: Refactor code X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=refs%2Fheads%2Fmaster-media.cgi;p=people%2Fstevee%2Fipfire-2.x.git media.cgi: Refactor code * Refactor the code to use filesystem-functions.pl * Fix display issues when using BTRFS * Avoid from spawning subshells over and over * Code cleanup Signed-off-by: Stefan Schantl --- diff --git a/html/cgi-bin/media.cgi b/html/cgi-bin/media.cgi index c1914f469..36cc73042 100644 --- a/html/cgi-bin/media.cgi +++ b/html/cgi-bin/media.cgi @@ -2,7 +2,7 @@ ############################################################################### # # # IPFire.org - A linux based firewall # -# Copyright (C) 2007-2022 IPFire Team # +# 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 # @@ -29,41 +29,50 @@ require '/var/ipfire/general-functions.pl'; require "${General::swroot}/lang.pl"; require "${General::swroot}/header.pl"; require "${General::swroot}/graphs.pl"; +require "${General::swroot}/filesystem-functions.pl"; my %color = (); my %mainsettings = (); &General::readhash("${General::swroot}/main/settings", \%mainsettings); &General::readhash("/srv/web/ipfire/html/themes/ipfire/include/colors.txt", \%color); -#workaround to suppress a warning when a variable is used only once -my @dummy = ( ${Header::colourred} ); -undef (@dummy); - my %cgiparams=(); -my @devices = `ls -1 /sys/block | grep -E '^sd|^mmcblk|^nvme|^xvd|^vd|^md' | sort | uniq`; +# Get all known block devices. +my @devices = &Filesystem::get_block_devices(); + +# Gather filesystem information +my %volumes = &Filesystem::volumes_status(df => "True", inodes => "True"); + +# Create sorted array which contains the volume names. +my @volumes = sort keys %volumes; + +# Gather iostats. +my %iostats = &iostats(); &Header::showhttpheaders(); &Header::openpage($Lang::tr{'media information'}, 1, ''); &Header::openbigbox('100%', 'left'); -foreach (@devices) { - my $device = $_; - chomp($device); - my @array = split(/\//,$device); - &Header::openbox('100%', 'center', "$array[$#array] $Lang::tr{'graph'}"); - diskbox($array[$#array]); - &Graphs::makegraphbox("media.cgi",$array[$#array],"day"); +# Loop through the array of devices. +foreach my $device (@devices) { + # Open a new box for displaying the device details and graph. + &Header::openbox('100%', 'center', "$device $Lang::tr{'graph'}"); + + # Call function to show additional diskbox details. + &diskbox($device); + + # Show the graph. + &Graphs::makegraphbox("media.cgi",$device,"day"); + + # Close the device box. &Header::closebox(); } - &Header::openbox('100%', 'center', $Lang::tr{'disk usage'}); + print "\n"; -open(DF,'/bin/df -P -B M -x rootfs|'); -while(){ - if ($_ =~ m/^Filesystem/ ){ - print < @@ -74,34 +83,39 @@ while(){ END ; - }else{ - my ($device,$size,$used,$free,$percent,$mount) = split; - print < - - - - - - - -END -; - } + +# Loop through the array of known volumes. +foreach my $volume (@volumes) { + # Assign nice human read-able values. + my $name = $volume; + my $mountpoint = $volumes{$volume}{'mpoint'}; + + # Format into something different than bytes. + my $space_available = &General::formatBytes($volumes{$volume}{'avail'}); + my $space_used = &General::formatBytes($volumes{$volume}{'used'}); + my $space_free = &General::formatBytes($volumes{$volume}{'free'}); + + # Convert percent value into integer format and append a percent sign. + my $space_used_percent = int($volumes{$volume}{'percent_used'}) . "%"; + + # Skip swap devices. + next if ($volumes{$volume}{'filesystem'} eq "swap"); + + print "\n"; + print "\n"; + print "\n"; + print "\n"; + print "\n"; + print "\n"; + print "\n"; + print "\n"; + print "\n"; } -close DF; -print " @@ -112,102 +126,172 @@ while(){ END ; - }else{ - my ($device,$size,$used,$free,$percent,$mount) = split; - print < - - - - - - - -END -; - } + +# Loop through the array of known volumes. +foreach my $volume (@volumes) { + # Assign nice human read-able values. + my $name = $volume; + my $mountpoint = $volumes{$volume}{'mpoint'}; + my $inodes_available = $volumes{$volume}{'inodes'} || "-"; + my $inodes_free = $volumes{$volume}{'inodes_free'} || "-"; + my $inodes_used = $volumes{$volume}{'inodes_used'} || "-"; + + # Convert percent value into integer format and append a percent sign. + my $inodes_percent_used = int($volumes{$volume}{'inodes_percent_used'} || "0") . "%"; + + # Skip swap devices. + next if ($volumes{$volume}{'filesystem'} eq "swap"); + + print "\n"; + print "\n"; + print "\n"; + print "\n"; + print "\n"; + print "\n"; + print "\n"; + print "\n"; + print "\n"; } -close DF; -my @iostat1 = qx(/usr/bin/iostat -dm -p | grep -v "Linux" | awk '{print \$1}'); -my @iostat2 = qx(/usr/bin/iostat -dm -p | grep -v "Linux" | awk '{print \$6}'); -my @iostat3 = qx(/usr/bin/iostat -dm -p | grep -v "Linux" | awk '{print \$7}'); -print ""; -my $i=0; - -for(my $i = 1; $i <= $#iostat1; $i++){ - if ( $i eq '1' ){ - print ""; - }else{ - print ""; - } + +print "\n"; +print "\n"; +print "\n"; +print "\n"; +print "\n"; +print "\n"; + +# Loop through the array of known volumes. +foreach my $volume (@volumes) { + # Assign nice values and use format function to no use bytes. + my $read = &General::formatBytes($iostats{$volume}{'bytes_read'}); + my $written = &General::formatBytes($iostats{$volume}{'bytes_written'}); + + print ""; } + print "
$Lang::tr{'device'} $Lang::tr{'mounted on'}
$device$mount$size$used$free -END -; - &percentbar($percent); - print < - $percent
$name$mountpoint$space_available$space_used$space_free\n"; + &percentbar($space_used_percent); + print "$space_used_percent
 \n

$Lang::tr{'inodes'}

\n"; -open(DF,'/bin/df -P -i -x rootfs|'); -while(){ - if ($_ =~ m/^Filesystem/ ){ - print <
 \n

$Lang::tr{'inodes'}

\n"; +print <
$Lang::tr{'device'} $Lang::tr{'mounted on'}
$device$mount$size$used$free -END -; - &percentbar($percent); - print < -$percent
$name$mountpoint$inodes_available$inodes_used$inodes_free\n"; + &percentbar($inodes_percent_used); + print "$inodes_percent_used
 \n

$Lang::tr{'transfers'}

$Lang::tr{'device'}$Lang::tr{'MB read'}$Lang::tr{'MB written'}
$iostat1[$i]$iostat2[$i]$iostat3[$i]
 \n

$Lang::tr{'transfers'}

$Lang::tr{'device'}$Lang::tr{'MB read'}$Lang::tr{'MB written'}
$volume$read$written
\n"; &Header::closebox(); &Header::closebigbox(); &Header::closepage(); -sub percentbar -{ - my $percent = $_[0]; - my $fg = '#a0a0a0'; - my $bg = '#e2e2e2'; +sub percentbar ($) { + my ($percent) = @_; + my $fg = '#a0a0a0'; + my $bg = '#e2e2e2'; - if ($percent =~ m/^(\d+)%$/ ) - { - print < - -END -; - if ($percent eq "100%") { - print "" - } elsif ($percent eq "0%") { - print "" - } else { - print "" - } - print < -END -; - } + # Early exit if the given percent amount is not numerical and does not contain + # a percent sign. + return unless ($percent =~ m/^(\d+)%$/); + + print "\n"; + print "\n"; + + # Handle 100% + if ($percent eq "100%") { + print "\n"; + print "\n"; + print "\n"; + print "
\n"; + + # Handle 0% + } elsif ($percent eq "0%") { + print "\n"; + + # Handle everything between. + } else { + print "\n"; + } + + print "
\n"; } -sub diskbox { - my $disk = $_[0]; - chomp $disk; - my @status; - if (-e "/var/run/hddstatus"){ - open(DATEI, "; - close(DATEI); - - foreach (@diskstate){ - if ( $_ =~/$disk/ ){@status = split(/-/,$_);} - } +sub diskbox ($) { + my ($disk) = @_; + + # Early return if the hddstatus file does not exist. + return unless (-e "/var/run/hddstatus"); - if ( $status[1]=~/standby/){ + # Open the disk status file. + open(STATUS, ") { + # Skip lines which does not belong to the requested disk. + next unless ($_ =~/$disk/); + + # Split the line content and save it into the status array. + my ($d, $status) = split(/-/,$_); + + # Check if the status is standby. + if ( $status =~ /standby/) { + # Get time since the disk is in standby. my $ftime = localtime((stat("/var/run/hddshutdown-$disk"))[9]); - print"Disk $disk status:".$status[1]." ($Lang::tr{'since'} $ftime)"; - }else{ - print"Disk $disk status:".$status[1].""; + + # Print message with standby status and time. + print"Disk $disk status:" . $status . " ($Lang::tr{'since'} $ftime)"; + } else { + # Print message about disk is online. + print"Disk $disk status:" . $status . ""; } + + # Break the loop. + last; } + # Close file handle. + close(STATUS); + + # Get S.M.A.R.T details of the disk. my $smart = `/usr/local/bin/smartctrl $disk`; + + # Clenup output. $smart = &Header::cleanhtml($smart); - print < - -END -; + + print "
\n"; + print "\n"; +} + +sub iostats () { + my %iostats = (); + + # Open kernel diskstats file. + open(IOSTATS, "/proc/diskstats") or die "Could not open /proc/diskstats: $!\n"; + + # Loop through the stats. + while () { + # Remove newlines. + chomp($_); + + # Split the line into pieces by a single ore multiple spaces. + my @iostats = split(/\s+/, $_); + + # Assign human read-able values. + my $disk = "/dev/" . $iostats[3]; + my $sectors_read = $iostats[6]; + my $sectors_written = $iostats[10]; + + # Skip if the current entry is not part of the volumes array. + next unless (grep (/^$disk$/, @volumes)); + + # The diskstats information provided by the kernel contains the read and written sectors. + # A sector equals 1Kb / 2 or 512 bytes - so we have to multiply the grabbed values by 512 to + # convert them into bytes. + # + # Convert the read sectors into bytes and add it to the hash. + $iostats{$disk}{'bytes_read'} = $sectors_read * 512; + + # Convert the written sectors into bytes and add it to the hash. + $iostats{$disk}{'bytes_written'} = $sectors_written * 512; + } + + # Close the file handle. + close(IOSTATS); + + # Return the hash. + return %iostats; }