From patchwork Sat Sep 23 10:54:55 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stefan Schantl X-Patchwork-Id: 7247 Return-Path: Received: from mail01.ipfire.org (mail01.haj.ipfire.org [172.28.1.202]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384 client-signature ECDSA (secp384r1) client-digest SHA384) (Client CN "mail01.haj.ipfire.org", Issuer "R3" (verified OK)) by web04.haj.ipfire.org (Postfix) with ESMTPS id 4Rt5d66kk1z3ws3 for ; Sat, 23 Sep 2023 10:55:06 +0000 (UTC) Received: from mail02.haj.ipfire.org (mail02.haj.ipfire.org [172.28.1.201]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) client-signature ECDSA (secp384r1)) (Client CN "mail02.haj.ipfire.org", Issuer "R3" (verified OK)) by mail01.ipfire.org (Postfix) with ESMTPS id 4Rt5d521sgzx8; Sat, 23 Sep 2023 10:55:05 +0000 (UTC) Received: from mail02.haj.ipfire.org (localhost [127.0.0.1]) by mail02.haj.ipfire.org (Postfix) with ESMTP id 4Rt5d51MfRz2yCr; Sat, 23 Sep 2023 10:55:05 +0000 (UTC) Received: from mail01.ipfire.org (mail01.haj.ipfire.org [172.28.1.202]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-384) client-signature ECDSA (P-384)) (Client CN "mail01.haj.ipfire.org", Issuer "R3" (verified OK)) by mail02.haj.ipfire.org (Postfix) with ESMTPS id 4Rt5d368V9z2xHS for ; Sat, 23 Sep 2023 10:55:03 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by mail01.ipfire.org (Postfix) with ESMTPSA id 4Rt5d25h6mzvy; Sat, 23 Sep 2023 10:55:02 +0000 (UTC) DKIM-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; d=ipfire.org; s=202003ed25519; t=1695466503; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding; bh=vudXOj3v2gjLiysEnuhXZDg10YjNP96SvMXMj8NWs8o=; b=yphqNiIzI/PBcgddbH7dVV0P5yjjMJrC5b3qpSufJyQy9lGknBdV8nz+peZql+yxaFGzJH Q11/FWBVE9LuVjBQ== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ipfire.org; s=202003rsa; t=1695466503; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding; bh=vudXOj3v2gjLiysEnuhXZDg10YjNP96SvMXMj8NWs8o=; b=Xd+kk/0QZ/lS38I+tVyuCX6xp+6K9ke+TpMtflzxeULjKWIxqohUMhph91IHyMHpp5l8s1 v8tFYyHq3Z7ht7zYT0MnZYBCRyhOJaXzy7djXnSHFEZp3Yw3lEkZ+CtR8f4Nz/twMhwl66 G2cIQKDGkJ9KVQir8yNEynHlXiMtUpdVJIONRQhG6aw0nuH5Ugz2/Z/TAICtZZu747+E9O Qk77APPRIfj0faPbDdFyn25NxDbC1273Hz8M+rgV7wtYzdJxI/29835gb64xC5Oc1ssqya PN0XebQg9sFQvgN+B6XsXc4e3v8Cit6g40cSSj3tBBw1dcaoeyy/QC0CwtoxvQ== From: Stefan Schantl To: development@lists.ipfire.org Subject: [PATCH] extrahd.cgi: Add support for LVM and MDADM devices Date: Sat, 23 Sep 2023 12:54:55 +0200 Message-Id: <20230923105455.4960-1-stefan.schantl@ipfire.org> MIME-Version: 1.0 X-BeenThere: development@lists.ipfire.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: IPFire development talk List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: development-bounces@lists.ipfire.org Sender: "Development" This commit adds support for using LVM and mdadm based RAID devices for the CGI page. In case one or more drives/partitions are used by such a "grouped" volume they still will displayed on the page, but can not be configured/used. Instead the "master" volume of which the drive/partition is part of is shown in the "mountpoint" input box. Signed-off-by: Stefan Schantl --- html/cgi-bin/extrahd.cgi | 303 +++++++++++++++++++++++++++++---------- 1 file changed, 225 insertions(+), 78 deletions(-) diff --git a/html/cgi-bin/extrahd.cgi b/html/cgi-bin/extrahd.cgi index c3a14c9e4..afe79479b 100644 --- a/html/cgi-bin/extrahd.cgi +++ b/html/cgi-bin/extrahd.cgi @@ -51,6 +51,12 @@ my @devices = &get_block_devices(); # Grab all known UUID's. my %uuids = &get_uuids(); +# Detect device mapper devices. +my %device_mapper = &get_device_mapper(); + +# Grab members of group devices (RAID, LVM) +my %grouped_devices = &collect_grouped_devices(); + # Grab all mountpoints. my %mountpoints = &get_mountpoints(); @@ -236,102 +242,144 @@ END # Grab the known partitions of the current block device. my @partitions = &get_device_partitions($device); - foreach my $partition (@partitions) { - my $disabled; + # Check if the block device has any partitions for display. + if (@partitions) { + # Loop through the partitions. + foreach my $partition (@partitions) { + # Call function to display the row in the WUI. + &print_row($partition); + } + } + + # Also print rows for devices with an UUID. + &print_row($device) if($uuids{$device}); + } - # Omit the partition size. - my $bsize = &get_device_size($partition); + print <  +   + $Lang::tr{'extrahd install or load driver'} + +END +; - # Convert into human-readable format. - my $size = &General::formatBytes($bsize); +&Header::closebox(); - # Try to omit the used filesystem. - my $fs = $filesystems{$partition}; +&Header::closebigbox(); +&Header::closepage(); - # Get the mountpoint. - my $mountpoint = $mountpoints{$partition}; - # If no mountpoint could be determined try to grab from - # configured drives. - unless($mountpoint) { - my $uuid = $uuids{$partition}; +# +# Function to print a table row with device data on the WUI. +# +sub print_row ($) { + my ($partition) = @_; - # Build uuid string. - $uuid = "UUID=" . $uuid; + my $disabled; - # Try to obtain a possible moutpoint from configured drives. - $mountpoint = $configured_drives{$uuid} if ($configured_drives{$uuid}); - } + # Omit the partition size. + my $bsize = &get_device_size($partition); - # Check if the mountpoint is used as root or boot device. - if ($mountpoint eq "/" or $mountpoint =~ "^/boot") { - $disabled = "disabled"; + # Convert into human-readable format. + my $size = &General::formatBytes($bsize); - # Check if it is mounted. - } elsif(&is_mounted($mountpoint)) { - $disabled = "disabled"; + # Try to omit the used filesystem. + my $fs = $filesystems{$partition}; - # Check if the device is used as swap. - } elsif (&is_swap($partition)) { - $disabled = "disabled"; - $mountpoint = "swap"; - $fs = "swap"; - } + # Get the mountpoint. + my $mountpoint = $mountpoints{$partition}; - print < - UUID=$uuids{$partition} - - /dev/$partition - $Lang::tr{'size'} $size - $fs - - - - -END -; - # Check if the mountpoint refers to a known configured drive. - if(&is_configured($mountpoint)) { - print "\n"; - print "\n"; - - # Check if the device is mounted properly. - if(&is_mounted($mountpoint)) { - print "$Lang::tr{ \n"; - } else { - print "$Lang::tr{ \n"; - } - - print "\n"; - } else { - unless($disabled) { - print "\n"; - print "\n"; - print "$Lang::tr{ \n"; - print "\n"; - } - } - - print < -END -; } + # Generate partition string. + my $partition_string = "/dev/$partition"; + # Check if the given partition is managed by device mapper. + if (exists($device_mapper{$partition})) { + # Alter the partition string to used one by the device mapper. + $partition_string = "$device_mapper{$partition}"; + } + + # Check if the device is part of a group. + my $grouped_device = &is_grouped_member($partition); + + # If no mountpoint could be determined try to grab from + # configured drives. + unless($mountpoint) { + my $uuid = $uuids{$partition}; + + # Build uuid string. + $uuid = "UUID=" . $uuid; + + # Try to obtain a possible moutpoint from configured drives. + $mountpoint = $configured_drives{$uuid} if ($configured_drives{$uuid}); + } + + # Check if the mountpoint is used as root or boot device. + if ($mountpoint eq "/" or $mountpoint =~ "^/boot") { + $disabled = "disabled"; + + # Check if it is mounted. + } elsif(&is_mounted($mountpoint)) { + $disabled = "disabled"; + + # Check if the device is used as swap. + } elsif (&is_swap($partition)) { + $disabled = "disabled"; + $mountpoint = "swap"; + $fs = "swap"; + + # Check if the device is part of a group. + } elsif ($grouped_device) { + $disabled = "disabled"; + $mountpoint = "/dev/$grouped_device"; + $mountpoint = $device_mapper{$grouped_device} if (exists($device_mapper{$grouped_device})); + } + + print "
\n"; + + # Only display UUID details if an UUID could be obtained. + if ( $uuids{$partition} ) { + print "UUID=$uuids{$partition}\n"; } print <  -   - $Lang::tr{'extrahd install or load driver'} - + + + $partition_string + $Lang::tr{'size'} $size + $fs + + + + END ; -&Header::closebox(); + # Check if the mountpoint refers to a known configured drive. + if(&is_configured($mountpoint)) { + print "\n"; + print "\n"; + + # Check if the device is mounted properly. + if(&is_mounted($mountpoint)) { + print "$Lang::tr{ \n"; + } else { + print "$Lang::tr{ \n"; + } -&Header::closebigbox(); -&Header::closepage(); + print "\n"; + } else { + unless($disabled) { + print "\n"; + print "\n"; + print "$Lang::tr{ \n"; + print "\n"; + } + } + + print < +END +; +} # ## Function which return an array with all available block devices. @@ -486,6 +534,86 @@ sub get_device_size ($) { return $size; } +# +## Function which tries to detect if a block device is a device mapper device and returns the alias a +## a hash. Example: "dm-0" -> "/dev/mapper/GROUP-DEVICE" +# +sub get_device_mapper () { + my %mapper_devices = (); + + # Loop through all known block devices. + foreach my $block_device (@devices) { + # Generate device directory. + my $device_dir = "$sysfs_block_dir/$block_device"; + + # Skip the device if it is not managed by device mapper + # In this case the "bd" is not present. + next 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 = ; + close(NAME); + + # Skip device if no name could be determined. + next 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"; + + # Store the device and the omited mapper name in the hash. + $mapper_devices{$block_device} = $dev_path; + } + + # Return the hash of omited device mapper devices. + return %mapper_devices; +} + +# +## Function which will collect grouped devices and their members as array in a hash and returns them. +## For example: "sda1" -> "dm-0" in case /dev/sda1 is assigned to a device mapper group. +# +sub collect_grouped_devices () { + my %grouped_devices = (); + + # Loop through the array of known block devices. + foreach my $device (@devices) { + # 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. + next 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 ".."); + + # Add the found member to the array of members. + push(@members, $_); + } + + closedir(MEMBERS); + + # Skip the device if no members could be grabbed. + next unless (@members); + + # Add the array of found members as value to the hash of grouped devices. + $grouped_devices{$device} = [ @members ]; + } + + # Return the hash of found grouped devices and their members. + return %grouped_devices; +} + # ## Function which returns all currently mounted devices as a hash. ## example: "sda1" -> "/boot" @@ -708,3 +836,22 @@ sub is_configured ($) { return 1 if($configured_drives{$uuid} eq "$path"); } } + +# +## Retruns the device name of the grouped device,if a given device is a group member. +# +sub is_grouped_member ($) { + my ($device) = @_; + + # Loop through the hash of found grouped devices. + foreach my $grouped_device(keys %grouped_devices) { + # The found members are stored as arrays. + my @members = @{ $grouped_devices{$grouped_device} }; + + # Loop through array of members and check if the given + # device is part of it. + foreach my $member (@members) { + return $grouped_device if ($member eq $device); + } + } +}