]> git.ipfire.org Git - people/stevee/ipfire-2.x.git/commitdiff
filesystem-functions.pl: New perl functions library
authorStefan Schantl <stefan.schantl@ipfire.org>
Wed, 1 May 2024 11:00:14 +0000 (13:00 +0200)
committerStefan Schantl <stefan.schantl@ipfire.org>
Wed, 1 May 2024 11:00:14 +0000 (13:00 +0200)
This library contains various functions to gather device and filesystem
details.

Signed-off-by: Stefan Schantl <stefan.schantl@ipfire.org>
config/cfgroot/filesystem-functions.pl [new file with mode: 0644]
config/rootfiles/common/configroot
lfs/configroot

diff --git a/config/cfgroot/filesystem-functions.pl b/config/cfgroot/filesystem-functions.pl
new file mode 100644 (file)
index 0000000..2c3877a
--- /dev/null
@@ -0,0 +1,456 @@
+#!/usr/bin/perl -w
+###############################################################################
+#                                                                             #
+# IPFire.org - A linux based firewall                                         #
+# Copyright (C) 2007-2024  IPFire Team  <info@ipfire.org>                     #
+#                                                                             #
+# 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 2 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 <http://www.gnu.org/licenses/>.       #
+#                                                                             #
+###############################################################################
+
+package Filesystem;
+
+use strict;
+
+# Import perl module to get details about the space usage.
+use Filesys::Df;
+
+# SYSFS directory which contains all block device data.
+my $sysfs_block_dir = "/sys/class/block";
+
+# Directory where the uuid mappings can be found.
+my $uuid_dir = "/dev/disk/by-uuid";
+
+#
+## Function which returns a lot of details about the known devices
+## as a hash of hashes.
+##
+## Please note that all size values are in bytes!
+##
+## Passing the following options as a hash allows to customize what
+## should be returned:
+## 
+## * devices => "all" - To get details about all known devices
+## * uuids => "True" - To also grab the UUIDS for each device
+## * df => "True" - To grab details about the filesystem usage
+##
+## Example: {'/dev/vda1' => {
+#                      'free' => '1234567890',
+#                      'size' => '1234567890',
+#                      'filesystem' => 'ext4',
+#                      'avail' => '1234567890',
+#                      'percent_free' => 60,
+#                      'percent_used' => 40,
+#                      'mount_options' => 'rw,noatime,....',
+#                      'used' => '1234567890',
+#                      'mpoint' => '/somewhere'
+#                      }
+#      };
+#
+sub volumes_status (%) {
+       my (%options) = @_;
+       my %volumes;
+
+       # Grab all available devices if requested.
+       if($options{'devices'} eq "all") {
+               # Get all available devices.
+               my @devices = &get_block_devices();
+
+               # Loop through the array of devices.
+               foreach my $device (@devices) {
+                       my $dev_name = "/dev/" . "$device";
+
+                       # Get the device vendor.
+                       my $vendor = &get_device_vendor($device);
+
+                       # Add the grabbed vendor to the hash of volumes.
+                       $volumes{$dev_name}{'vendor'} = $vendor if($vendor);
+
+                       # Get the device model.
+                       my $model = &get_device_model($device);
+
+                       # Add the grabbed model to the hash of volumes.
+                       $volumes{$dev_name}{'model'} = $model if($model);
+
+                       # Grab the size of the device.
+                       my $size = &get_device_size($device);
+
+                       # Add the size to the hash of volumes.
+                       $volumes{$dev_name}{'size'} = $size if($size);
+
+                       # Try to obtain the device mapper (LVM) group name.
+                       my $lvm_group = &get_device_mapper_group($device);
+
+                       # Add the grabbed lvm group name to the hash of volumes.
+                       $volumes{$dev_name}{'lvm_group'} = $lvm_group if($lvm_group);
+
+                       # Try to grab the members in case it is a grouped device (mdraid).
+                       my @mdraid_members = &get_mdraid_members($device);
+
+                       # Add the mdraid members to the hash of volumes.
+                       $volumes{$dev_name}{'mdraid_members'} = [ @mdraid_members ] if(@mdraid_members);
+
+                       # Grab the device partitions.
+                       my @partitions = &get_device_partitions($device);
+                       my @partlist = ();
+
+                       # Loop through the array of partitions.
+                       foreach my $partition(@partitions) {
+                               my $dev_name = "/dev/" . "$partition";
+
+                               # Add the partition to the partlist array.
+                               push(@partlist, $dev_name);
+
+                               # Grab the size of the partition.
+                               my $size = &get_device_size($partition);
+
+                               # Add the size to the hash of volumes.
+                               $volumes{$dev_name}{'size'} = $size if ($size);
+                       }
+
+                       # Add the array of partitions to the hash of volumes.
+                       $volumes{$dev_name}{'partitions'} = [ @partlist ] if (@partlist);
+               }
+       }
+
+       # Obtain the uuids if requested.
+       if ($options{'uuids'} eq "True") {
+               # Open uuid directory and read-in the current known uuids.
+               opendir(UUIDS, "$uuid_dir");
+
+               # Loop through the uuids.
+               foreach my $uuid (readdir(UUIDS)) {
+                       # Skip . and ..
+                       next if($uuid eq "." or $uuid eq "..");
+
+                       # Skip everything which is not a symbolic link.
+                       next unless(-l "$uuid_dir/$uuid");
+
+                       # Resolve the target of the symbolic link.
+                       my $target = readlink("$uuid_dir/$uuid");
+
+                       # Split the link target into pieces.
+                       my @tmp = split("/", $target);
+
+                       # Assign the last element of the array to the dev variable.
+                       my $device = "/dev/" . "$tmp[-1]";
+
+                       # Add the found uuid to the volumes hash.
+                       $volumes{$device}{'uuid'} = $uuid unless($volumes{$device}{'uuid'});
+               }
+
+               # Close directory handle.
+               closedir(UUIDS);
+       }
+
+       # Open and read-in the current mounts from the
+       # kernel file system.
+       open(MOUNT, "/proc/mounts");
+
+       # Loop through the known mounts.
+       while(<MOUNT>) {
+               # Skip mounts which does not belong to a device.
+               next unless ($_ =~ "^/dev");
+
+               # Cut the line into pieces and assign nice variables.
+               my ($dev, $mpoint, $fs, $options, $a, $b) = split(/ /, $_);
+
+               # Check if the processed device is not part of the hash and
+               # add the grabbed data.
+               $volumes{$dev}{'filesystem'} = $fs unless($volumes{$dev}{'filesystem'});
+               $volumes{$dev}{'mount_options'} = $options unless($volumes{$dev}{'mount_options'});
+               $volumes{$dev}{'mpoint'} = $mpoint unless($volumes{$dev}{'mpoint'});
+
+               if ($options{'df'} eq "True") {
+                       # Call df module to get details about space usage
+                       # and request the output in bytes (1).
+                       my $df = Filesys::Df::df($mpoint, 1);
+
+                       # Assign grabbed storrage details to the hash.
+                       if (defined($df)) {
+                               $volumes{$dev}{"size"} = $df->{blocks};
+                               $volumes{$dev}{"free"} = $df->{bfree};
+                               $volumes{$dev}{"avail"} = $df->{bavail};
+                               $volumes{$dev}{"used"} = $df->{used};
+                               $volumes{$dev}{"percent_used"} = $df->{per};
+                               $volumes{$dev}{"percent_free"} = int(100) - $df->{per};
+
+                               # Undefine created df object for the processed mountpoint.
+                               undef $df;
+                       }
+                }
+        }
+
+        # Close file handle.
+        close(MOUNT);
+
+       # Open and read the swaps file.
+       open(SWAP, "/proc/swaps");
+
+       # Loop though the file content.
+       while(<SWAP>) {
+               # Skip lines which does not belong to a device.
+               next unless ($_ =~ "^/dev");
+
+               # Split the line and assign nice variables.
+               my ($dev, $type, $size, $used, $prio) = split(/ /, $_);
+
+               # Assign "swap" as filesystem if the volume is used as swap.
+               $volumes{$dev}{"filesystem"} = "swap" unless($volumes{$dev}{"filesystem"});
+       }
+
+       # Close file handle.
+       close(SWAP);
+
+       # Return the hash of hashes.
+       return %volumes;
+}
+
+#
+## Returns the vendor of a given block device.
+#
+sub get_device_vendor ($) {
+       my ($device) = @_;
+
+       # Assign device directory.
+       my $device_dir = "$sysfs_block_dir/$device";
+
+       # Abort and return nothing if the device dir does not exist
+       # or no vendor file exists.
+       return unless(-d "$device_dir");
+       return unless(-f "$device_dir/device/vendor");
+
+       # Open and read-in the device vendor.
+       open(VENDOR, "$device_dir/device/vendor");
+       my $vendor = <VENDOR>;
+       close(VENDOR);
+
+       # Abort and return nothing if no vendor could be read.
+       return unless($vendor);
+
+       # Remove any newlines from the vendor string.
+       chomp($vendor);
+
+       # Return the omited vendor.
+       return $vendor;
+}
+
+#
+## Returns the model name (string) of a given block device.
+#
+sub get_device_model ($) {
+       my ($device) = @_;
+
+       # Assign device directory.
+       my $device_dir = "$sysfs_block_dir/$device";
+
+       # Abort and return nothing if the device dir does not exist
+       # or no model file exists.
+       return unless(-d "$device_dir");
+       return unless(-f "$device_dir/device/model");
+
+       # Open and read-in the device model.
+       open(MODEL, "$device_dir/device/model");
+       my $model = <MODEL>;
+       close(MODEL);
+
+       # Abort and return nothing if no model could be read.
+       return unless($model);
+
+       # Remove any newlines from the model string.
+       chomp($model);
+
+       # Return the model string.
+       return $model;
+}
+
+#
+## Returns the size of a given device in bytes.
+#
+sub get_device_size ($) {
+       my ($device) = @_;
+
+       # Assign device directory.
+       my $device_dir = "$sysfs_block_dir/$device";
+
+       # Abort and return nothing if the device dir does not exist
+       # or no size file exists.
+       return unless(-d "$device_dir");
+       return unless(-f "$device_dir/size");
+
+       # Open and read-in the device size.
+       open(SIZE, "$device_dir/size");
+       my $size = <SIZE>;
+       close(SIZE);
+
+       # Abort and return nothing if the size could not be read.
+       return unless($size);
+
+       # Remove any newlines for the size string.
+       chomp($size);
+
+       # The omited size only contains the amount of blocks from the
+       # given device. To convert this into bytes we have to multiply this
+       # value with 512 bytes for each block. This is a static value used by
+       # the linux kernel.
+       $size = $size * 512;
+
+       # Return the size in bytes.
+       return $size;
+}
+
+#
+## Function which return an array with all available block devices.
+#
+sub get_block_devices () {
+       my @devices;
+
+       # Open directory from kernel sysfs.
+       opendir(DEVICES, "/sys/block");
+
+       # Loop through the directory.
+       while(readdir(DEVICES)) {
+               # Skip . and ..
+               next if($_ =~ /^\.$/);
+               next if($_ =~ /^\..$/);
+
+               # Skip any loopback and ram devices.
+               next if($_ =~ "^loop");
+               next if($_ =~ "^ram");
+
+               # Add the device to the array of found devices.
+               push(@devices, $_);
+       }
+
+       # Close directory handle.
+       closedir(DEVICES);
+
+       # Sort the block devices alphabetically.
+       @devices = sort(@devices);
+
+       # Return the devices array.
+       return @devices;
+}
+
+#
+## Function which return all partitions of a given block device.
+#
+sub get_device_partitions ($) {
+       my ($device) = @_;
+
+       # Array to store the known partitions for the given
+       # device.
+       my @partitions;
+
+       # Assign device directory.
+       my $device_dir = "$sysfs_block_dir/$device";
+
+       # Abort and return nothing if the device dir does not exist.
+       return unless(-d "$device_dir");
+
+       # Open device directory.
+       opendir(DEVICE, "$sysfs_block_dir/$device");
+
+       # Loop through the directory
+       while(readdir(DEVICE)) {
+               # Skip everything which does not start with the device name.
+               next unless($_ =~ "^$device");
+
+               # Add found partition to the array of partitions.
+               push(@partitions, $_);
+       }
+
+       # Close directory handle.
+       closedir(DEVICE);
+
+       # Sort the array of partitions.
+       @partitions = sort(@partitions);
+
+       # Return the sortet array.
+       return @partitions;
+}
+
+#
+## Function which will collect the members of a grouped device (mdraid) and returns them as array.
+#
+sub get_mdraid_members ($) {
+       my ($device) = @_;
+
+       # Generate device directory.
+       my $device_dir = "$sysfs_block_dir/$device";
+
+       # Skip device if it has no members.
+       # In this case the "slaves" directory does not exist.
+       return unless (-e "$device_dir/slaves");
+
+       # Tempoarary array to store the members of a group.
+       my @members = ();
+
+       # Grab all members.
+       opendir(MEMBERS, "$device_dir/slaves");
+       while(readdir(MEMBERS)) {
+               next if($_ eq ".");
+               next if($_ eq "..");
+
+               # Format found member with full /dev path.
+               my $member = "/dev/" . "$_";
+
+               # Add the found member to the array of members.
+               push(@members, $member);
+       }
+
+       closedir(MEMBERS);
+
+       # Skip the device if no members could be grabbed.
+       return unless (@members);
+
+       # Sort the members in alpabetical order.
+       @members = sort(@members);
+
+       # Return the sorted array.
+       return @members;
+}
+
+#
+## Function which tries to obtain the group name in case it is managed by the device mapper (LVM).
+#
+sub get_device_mapper_group ($) {
+       my ($device) = @_;
+
+       # Generate  device directory.
+       my $device_dir = "$sysfs_block_dir/$device";
+
+       # Skip the device if it is not managed by device mapper
+       # In this case the "bd" is not present.
+       return unless (-e "$device_dir/dm");
+       
+       # Grab the group and volume name.
+       open(NAME, "$device_dir/dm/name") if (-e "$device_dir/dm/name");
+       my $name = <NAME>;
+       close(NAME);
+
+       # Skip device if no name could be determined.
+       return unless($name);
+
+       # Remove any newlines from the name string.
+       chomp($name);
+
+       # Generate path to the dev node in devfs.
+       my $dev_path = "/dev/mapper/$name";
+
+       # Return the path.
+       return $dev_path;
+}
+
+1;
index a286a15383393aa753b359fc9477c324e01bb977..5e4c9744ed3924f02b5d4282ee18951a4d8cf0e0 100644 (file)
@@ -58,6 +58,7 @@ var/ipfire/extrahd/bin/extrahd.pl
 #var/ipfire/extrahd/partitions
 #var/ipfire/extrahd/scan
 #var/ipfire/extrahd/settings
+var/ipfire/filesystem-functions.pl
 var/ipfire/firewall
 #var/ipfire/firewall/config
 #var/ipfire/firewall/input
index 9f6c1ff8ca1a3d5e883c604618d64e1d3fafbc0f..1f2288c729c6a17f0e12dcbf32e8ccc5be2631d8 100644 (file)
@@ -81,6 +81,7 @@ $(TARGET) :
        cp $(DIR_SRC)/config/cfgroot/location-functions.pl      $(CONFIG_ROOT)/
        cp $(DIR_SRC)/config/cfgroot/ipblocklist-functions.pl   $(CONFIG_ROOT)/
        cp $(DIR_SRC)/config/cfgroot/ids-functions.pl           $(CONFIG_ROOT)/
+       cp $(DIR_SRC)/config/cfgroot/filesystem-functions.pl    $(CONFIG_ROOT)/
        cp $(DIR_SRC)/config/cfgroot/lang.pl                    $(CONFIG_ROOT)/
        cp $(DIR_SRC)/config/cfgroot/countries.pl               $(CONFIG_ROOT)/
        cp $(DIR_SRC)/config/cfgroot/graphs.pl                  $(CONFIG_ROOT)/