├── CHANGELOG.txt ├── CREDITS.txt ├── LICENCE.txt ├── README.md └── disklist.pl /CHANGELOG.txt: -------------------------------------------------------------------------------- 1 | Change Log 2 | ========== 3 | 4 | version 1.1c (19/06/2020) 5 | ------------ 6 | fix: 7 | - The column 'M' show the zfs mount point. The altroot (typically /mnt) is fetched from 'zpool list'. 8 | But now the 'zpool list' output can have an extra column 'CKPOINT' that we should take care for finding the 'ALTROOT' column. 9 | - Help fix for use "example" instead of "exemple" 10 | 11 | layout: 12 | - new column "h" giving the "Power on Hours" of devices. It's a smartctl's column, so for get it, you have to use the switch '-smartctl' 13 | - "-all" profil is now pPlzZMdtDUTSRHhemosX 14 | - "-long" profile is now plzdDUTSRHemosX 15 | 16 | 17 | 18 | version 1.1b (18/06/2020) 19 | ------------ 20 | fix: 21 | - strip non alphanumeric characters in S/N to align data from sas backplane and geom disk list 22 | 23 | 24 | 25 | version 1.1a (12/06/2019) 26 | ------------ 27 | 28 | fix: 29 | - fix display of unused disks (when not partitioned). In 1.1 some partitioned disks were also reported as unused disks 30 | 31 | core: 32 | - gptid association are now done with "glabel list" + "geli list" for handling encrypted partitions (GELI) 33 | - zpool list are now used for determine the pool mount point (from the altroot) 34 | 35 | layout: 36 | - columns without any values are automatically removed from the layout. 37 | For example, if you don't have multipath drives, subsequents columns are automatically removed from the final layout 38 | This behaviour may be disabled using switch -static 39 | - new column "M" giving the mount point of the zfs pool associated to the specified partition 40 | - new column "X" giving the encryption algorithm used for encrypt the specified partition 41 | - "-all" profil is now pPlzZMdtDUTSRHXemos 42 | - "-long" profile is now plzdDUTSRHXemos 43 | 44 | enhance: 45 | - new switch -locate allow to blink disk enclosure led for 10 seconds 46 | This switch works only for drive connected to a sas hba (sas2ircu & sas3ircu compatible controllers) 47 | - switch -multi: can be set multiple times. 48 | For example for listing physical drive only once you can use these arguments: -multi:none -multi:active -nolabel 49 | - new switch "-sort:" that allow to define the sort order of partitions and devices 50 | - new switch "-static" for prevent columns to be removed even if they are empties. 51 | - new switch "-nolabel" that allow to report only disk information without information about partitions 52 | - print at the end of the report the number of disks reported 53 | 54 | version 1.1 (07/06/2019) 55 | ----------- 56 | 57 | core: 58 | - don't use anymore "glabel status" 59 | - replace "gmultipath status" by "gmultipath list" 60 | - use "gpart list" for building the partitions table 61 | - use "gmultipath list" for completing the partitions table & devices with multipath datas 62 | 63 | disk type detection: 64 | - Handle NVME disk detection 65 | - Handle NVRAM disk detection 66 | 67 | new columns: 68 | - column "fs" that show the partition type (freebsd-zfs, freebsd-swap, bios-boot, ...) 69 | - column "path-state" that show the multipath state's device (OPTIMAL, ...) 70 | - column "temp" that show the disk temperature 71 | - column "zpool-location" that show the location of the partition inside the pool structure (tank/mirror-0, ...) 72 | 73 | new switches: 74 | - switch -smartctl that enable to report columns from smartctl tool (like temperature). without this switch theses columns are skipped. 75 | 76 | updated profil layout: 77 | - "all" report columns : pPlzZdtDUTSRHemos 78 | - "long" report columns : plzdDUTSRH 79 | - "short" report columns : pzdDUT 80 | 81 | filters: 82 | - the -fs: filter can now handle theses types: none, all, any, zfs, swap, bios, unknown 83 | - many filters can now be set more than one time for accepting more than one value. This apply to [-i: ], [-fs:], [-c:] 84 | 85 | layout switchs 86 | - profil switch like -all, -long or -short can be used in addition of the switch [-c:]. Theses switch are merged together for defining columns to report. -------------------------------------------------------------------------------- /CREDITS.txt: -------------------------------------------------------------------------------- 1 | 2 | Special Thanks to: 3 | > Pierre-Marie Fort who helped me writing the first version of this script (learn me perl by the force ^^) 4 | > Rand_ (from Serve The Home) for testing / feedbacks and help translations. 5 | > github.com/danielewood for patch contribution 6 | -------------------------------------------------------------------------------- /LICENCE.txt: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FreeNas-DiskList 2 | FreeNas "Disklist" script for report informations about installed hard drives 3 | 4 | I wrote this script for getting informations about all installed disks inside my FreeNas box. 5 | 6 | This script is written in Perl and use under the hood theses commands: 7 | * geom disk list 8 | * gpart list 9 | * glabel list 10 | * geli list 11 | * gmultipath list 12 | * zpool status 13 | * zpool list 14 | * sas2ircu list 15 | * sas2ircu # display 16 | * sas3ircu list 17 | * sas3ircu # display 18 | * smartctl -a [device] 19 | 20 | [Command line usages are available here](../../wiki/Command-Line-usage) 21 | 22 | # Examples 23 | 24 | ``` 25 | ./disklist.pl 26 | partition label zpool device disk size type serial rpm 27 | ----------------------------------------------------------------------------------------------------------------------------------------- 28 | da136p2 gptid/ee5188da-2104-11e6-937f-0cc47a320ec8 freenas-boot da136 SanDisk Ultra Fit 62 ??? 4C531001421128120501 ??? 29 | da128p2 gptid/a32d92ef-898d-11e6-92dc-0cc47a320ec8 volBACKUP da128 ATA Hitachi HUA72302 2000 HDD YGHGTR4A 7200 30 | da129p2 gptid/a64d2fdb-898d-11e6-92dc-0cc47a320ec8 volBACKUP da129 ATA Hitachi HUA72302 2000 HDD YFHLRZ1A 7200 31 | da130p2 gptid/a9830745-898d-11e6-92dc-0cc47a320ec8 volBACKUP da130 ATA Hitachi HUA72302 2000 HDD YGGU4MKA 7200 32 | da131p2 gptid/acad4d63-898d-11e6-92dc-0cc47a320ec8 volBACKUP da131 ATA Hitachi HUA72302 2000 HDD YGGU5D6A 7200 33 | da132p2 gptid/b00a4e80-898d-11e6-92dc-0cc47a320ec8 volBACKUP da132 ATA Hitachi HUA72302 2000 HDD YFGR7ZPC 7200 34 | da133p2 gptid/b35b860a-898d-11e6-92dc-0cc47a320ec8 volBACKUP da133 ATA Hitachi HUA72302 2000 HDD YGHHK07A 7200 35 | da134p2 gptid/b6b2f0e7-898d-11e6-92dc-0cc47a320ec8 volBACKUP da134 ATA Hitachi HUA72302 2000 HDD YFHHMMXA 7200 36 | da135p2 gptid/ba039608-898d-11e6-92dc-0cc47a320ec8 volBACKUP da135 ATA Hitachi HUA72302 2000 HDD YGGK2R4A 7200 37 | ``` 38 | 39 | ``` 40 | ./disklist.pl -i:zpool tank -all 41 | 42 | partition fs label zpool zpool-location device sector disk size type serial rpm sas-location 43 | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 44 | da128p2 freebsd-zfs gptid/a32d92ef-898d-11e6-92dc-0cc47a320ec8 tank tank/raidz2-0 da128 512 ATA Hitachi HUA72302 2000 HDD YGHGTR4A 7200 SAS2008(0):2#3 45 | da129p2 freebsd-zfs gptid/a64d2fdb-898d-11e6-92dc-0cc47a320ec8 tank tank/raidz2-0 da129 512 ATA Hitachi HUA72302 2000 HDD YFHLRZ1A 7200 SAS2008(0):2#2 46 | 47 | ``` 48 | -------------------------------------------------------------------------------- /disklist.pl: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/perl 2 | 3 | use strict; 4 | use warnings; 5 | use POSIX; 6 | #use XML::LibXML; 7 | 8 | #========================================================================== 9 | # 10 | # disklist.pl : Report installed disk output for FreeBsd/FreeNAS 11 | # Copyright (C) 2017 André Sébastien (sebastien.andre.288@gmail.com), Fort Pierre-Marie 12 | # 13 | # This program is free software: you can redistribute it and/or modify 14 | # it under the terms of the GNU Lesser General Public License as published by 15 | # the Free Software Foundation, either version 3 of the License, or any later version. 16 | # 17 | # This program is distributed in the hope that it will be useful, 18 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | # GNU Lesser General Public License for more details. 21 | # 22 | # You should have received a copy of the GNU Lesser General Public License 23 | # along with this program. If not, see . 24 | # 25 | #========================================================================== 26 | 27 | # 28 | # $devices{'da14'}->{'device'} da14 29 | # $devices{'da14'}->{'sectorsize'} 512 30 | # $devices{'da14'}->{'disksize'} 2000398934016 31 | # $devices{'da14'}->{'serial'} YFH1H5TD 32 | # $devices{'da14'}->{'partitioned'} 0 or 1 33 | # $devices{'da14'}->{'type'} SSD or HDD or NVME 34 | # $devices{'da14'}->{'rpm'} 7200 35 | # $devices{'da14'}->{'multipath'} multipath/disk46 36 | # $devices{'da14'}->{'multipath_mode'} ACTIVE 37 | # $devices{'da14'}->{'multipath_state'} OPTIMAL 38 | # 39 | our %devices; 40 | our @devicesKeys; 41 | 42 | # 43 | # $partitions{'gptid/fe38f700-829a-11e7-810a-00074304a2f0'}->{'gptid'} gptid/247b7464-8cba-11e6-b58c-0cc47a320ec8 44 | # $partitions{'gptid/fe38f700-829a-11e7-810a-00074304a2f0'}->{'uuid'} 247b7464-8cba-11e6-b58c-0cc47a320ec8 45 | # $partitions{'gptid/fe38f700-829a-11e7-810a-00074304a2f0'}->{'partition'} multipath/disk30p2 46 | # $partitions{'gptid/fe38f700-829a-11e7-810a-00074304a2f0'}->{'type'} freebsd-zfs 47 | # $partitions{'gptid/fe38f700-829a-11e7-810a-00074304a2f0'}->{'component'} multipath/disk30 48 | # $partitions{'gptid/fe38f700-829a-11e7-810a-00074304a2f0'}->{'device'} da97 49 | # $partitions{'gptid/fe38f700-829a-11e7-810a-00074304a2f0'}->{'devices'} da97,da96 50 | # $partitions{'gptid/fe38f700-829a-11e7-810a-00074304a2f0'}->{'path'} multipath/disk30 51 | # $partitions{'gptid/fe38f700-829a-11e7-810a-00074304a2f0'}->{'zpool'} volFAST 52 | # $partitions{'gptid/fe38f700-829a-11e7-810a-00074304a2f0'}->{'zpool_location'} volMAIN/mirror-0 53 | # $partitions{'gptid/fe38f700-829a-11e7-810a-00074304a2f0'}->{'zpool_state'} ONLINE 54 | # $partitions{'gptid/fe38f700-829a-11e7-810a-00074304a2f0'}->{'zpool_mount'} /mnt/volFAST 55 | # $partitions{'gptid/fe38f700-829a-11e7-810a-00074304a2f0'}->{'encryption'} AES-XTS 56 | # 57 | our %partitions; 58 | our @partitionsKeys; 59 | 60 | # 61 | # $sasdevices{'YFJ4G2BD'}->{'sas'} sas3 62 | # $sasdevices{'YFJ4G2BD'}->{'controller'} 1 63 | # $sasdevices{'YFJ4G2BD'}->{'adapter'} SAS2008 64 | # $sasdevices{'YFJ4G2BD'}->{'enclosure'} 3 65 | # $sasdevices{'YFJ4G2BD'}->{'slot'} 55 66 | # $sasdevices{'YFJ4G2BD'}->{'serial'} YFJ4G2BD 67 | # $sasdevices{'YFJ4G2BD'}->{'protocol'} SAS 68 | # $sasdevices{'YFJ4G2BD'}->{'url'} sas2-0:1#6 69 | # 70 | our %sasdevices; 71 | our %sasdevicesLocate; 72 | 73 | # arguments 74 | # 75 | our @query_class; 76 | our @query_component; 77 | our @query_fs; 78 | our @query_disktype; 79 | our @query_multipath; 80 | our $accept_smartctl=0; 81 | our $accept_label=1; 82 | our $accept_collapse=1; 83 | our $output_columns=""; 84 | our $output_sort=""; 85 | our $output_layout="col"; 86 | our $locate=0; 87 | 88 | # report output layout 89 | # 90 | our @layout; 91 | 92 | 93 | sub parseDisks { 94 | # da70 95 | # 96 | my $disk; 97 | 98 | my $device = ""; 99 | my $type = ""; 100 | my $description = ""; 101 | my $sectorsize = ""; 102 | my $rpm = ""; 103 | my $disksize = ""; 104 | my $serial = ""; 105 | 106 | for( `geom disk list` ) { 107 | (my $line) = /^(.*)$/; 108 | 109 | if ( $line =~ m/^Geom name:/ ) { 110 | $device = $line; 111 | $device =~ s/^.*:[\s]*(.*)[\s]*$/$1/; 112 | $type = ""; 113 | $description = ""; 114 | $sectorsize = ""; 115 | $rpm = ""; 116 | $disksize = ""; 117 | $serial = ""; 118 | 119 | $disk = {}; 120 | $disk->{ 'device' } = $device; 121 | $disk->{ 'type' } = ""; 122 | $disk->{ 'description' } = ""; 123 | $disk->{ 'sectorsize' } = ""; 124 | $disk->{ 'rpm' } = ""; 125 | $disk->{ 'temperature' } = ""; 126 | $disk->{ 'poh' } = ""; 127 | $disk->{ 'disksize' } = ""; 128 | $disk->{ 'serial' } = ""; 129 | $disk->{ 'partitioned' } = 0; 130 | $disk->{ 'multipath' } = ""; 131 | $disk->{ 'multipath_mode' } = ""; 132 | $disk->{ 'multipath_state' } = ""; 133 | 134 | $devices{ $device } = $disk; 135 | } 136 | if ( $line =~ m/^Consumers:/ ) { 137 | $device = ""; 138 | $type = ""; 139 | $description = ""; 140 | $sectorsize = ""; 141 | $rpm = ""; 142 | $disksize = ""; 143 | $serial = ""; 144 | } 145 | if ( $line =~ m/^[\s]*descr:/ ) { 146 | $description = $line; 147 | $description =~ s/^.*:[\s]*(.*)[\s]*$/$1/; 148 | if ( $device ne "" ) { $disk->{ 'description' } = $description; } 149 | } 150 | if ( $line =~ m/^[\s]*Sectorsize:/ ) { 151 | $sectorsize = $line; 152 | $sectorsize =~ s/^.*:[\s]*(.*)[\s]*$/$1/; 153 | if ( $device ne "" ) { $disk->{ 'sectorsize' } = $sectorsize; } 154 | } 155 | if ( $line =~ m/^[\s]*rotationrate:/ ) { 156 | $rpm = $line; 157 | $rpm =~ s/^.*:[\s]*(.*)[\s]*$/$1/; 158 | if ( $device ne "" ) { 159 | $disk->{ 'rpm' } = $rpm; 160 | if ( $rpm =~ m/^[0-9]+$/ ) { 161 | if( $rpm > 0 ) { 162 | $disk->{ 'type' } = "HDD"; 163 | } 164 | else { 165 | $disk->{ 'type' } = "SSD"; 166 | } 167 | } 168 | else { 169 | $disk->{ 'type' } = "???"; 170 | $disk->{ 'rpm' } = "???"; 171 | } 172 | } 173 | } 174 | if ( $line =~ m/^[\s]*Mediasize:/ ) { 175 | $disksize = $line; 176 | $disksize =~ s/^.*:[\s]*([0-9]*)[\s]*.*$/$1/; 177 | if ( $device ne "" ) { $disk->{ 'disksize' } = $disksize; } 178 | } 179 | if ( $line =~ m/^[\s]*ident:/ ) { 180 | $serial = $line; 181 | $serial =~ s/^.*:[\s]*(.*)[\s]*$/$1/; 182 | $serial =~ s/[^a-zA-Z0-9]//g; 183 | if ( $device ne "" ) { $disk->{ 'serial' } = $serial; } 184 | } 185 | } 186 | } 187 | 188 | sub parsePartitions { 189 | my $inProviders = 0; 190 | my $inConsumers = 0; 191 | 192 | my $part = ""; 193 | my $type = ""; 194 | my $component = ""; 195 | my $uuid = ""; 196 | 197 | my %providers; 198 | 199 | for( `gpart list` ) { 200 | (my $line) = /^(.*)$/; 201 | 202 | if ( $line =~ m/^Providers:/ ) { 203 | $inProviders = 1; 204 | $inConsumers = 0; 205 | } 206 | if( $line =~ m/^Consumers:/ ) { 207 | $inProviders = 0; 208 | $inConsumers = 1; 209 | 210 | if ( $part ne "" ) { 211 | $providers{ $part }->{'gptid'} = ""; 212 | $providers{ $part }->{'uuid'} = $uuid; 213 | $providers{ $part }->{'partition'} = $part; 214 | $providers{ $part }->{'type'} = $type; 215 | } 216 | 217 | $uuid = ""; 218 | $part = ""; 219 | $type = ""; 220 | $component = ""; 221 | } 222 | 223 | if( ( $line =~ m/^\d+\.[\s]*Name:/ ) && ( $inProviders == 1 ) ) { 224 | if ( $part ne "" ) { 225 | $providers{ $part }->{'gptid'} = ""; 226 | $providers{ $part }->{'uuid'} = $uuid; 227 | $providers{ $part }->{'partition'} = $part; 228 | $providers{ $part }->{'type'} = $type; 229 | } 230 | 231 | $uuid = ""; 232 | $part = $line; 233 | $part =~ s/^\d+\.\sName:[\s]*(.*)[\s]*$/$1/; 234 | $type = ""; 235 | $component = ""; 236 | } 237 | if( ( $line =~ m/^[\s]*type:/ ) && ( $inProviders == 1 ) ) { 238 | $type = $line; 239 | $type =~ s/^[\s]*type:[\s]*(.*)[\s]*$/$1/; 240 | 241 | if( $type =~ m/^!/ ) { 242 | $type = "unknown"; 243 | } 244 | } 245 | if( ( $line =~ m/^[\s]*rawuuid:/ ) && ( $inProviders == 1 ) ) { 246 | $uuid = $line; 247 | $uuid =~ s/^[\s]*rawuuid:[\s]*(.*)[\s]*$/$1/; 248 | } 249 | 250 | if( ( $line =~ m/^\d+\.\sName:/ ) && ( $inConsumers == 1 ) ) { 251 | $component = $line; 252 | $component =~ s/^\d+\.\sName:[\s]*(.*)[\s]*$/$1/; 253 | 254 | foreach my $p ( keys %providers ) { 255 | my $ilabel = $providers{ $p }; 256 | 257 | $partitions{ $ilabel->{'uuid'} } = $providers{ $p }; 258 | $partitions{ $ilabel->{'uuid'} }->{'component'} = $component; 259 | $partitions{ $ilabel->{'uuid'} }->{'device'} = ""; 260 | $partitions{ $ilabel->{'uuid'} }->{'devices'} = ""; 261 | $partitions{ $ilabel->{'uuid'} }->{'zpool'} = ""; 262 | $partitions{ $ilabel->{'uuid'} }->{'zpool_location'} = ""; 263 | $partitions{ $ilabel->{'uuid'} }->{'zpool_state'} = ""; 264 | $partitions{ $ilabel->{'uuid'} }->{'zpool_mount'} = ""; 265 | $partitions{ $ilabel->{'uuid'} }->{'path'} = ""; 266 | $partitions{ $ilabel->{'uuid'} }->{'encryption'} = ""; 267 | 268 | if( exists( $devices{ $component } ) ) { 269 | $partitions{ $ilabel->{'uuid'} }->{'device'} = $component; 270 | $devices{ $component }->{'partitioned'} = 1; 271 | } 272 | } 273 | undef %providers; 274 | 275 | $inConsumers = 0; 276 | } 277 | } 278 | } 279 | 280 | sub parseLabels() { 281 | my $inProviders = 0; 282 | my $inConsumers = 0; 283 | 284 | my $gptid = ""; 285 | my $part = ""; 286 | 287 | for( `glabel list` ) { 288 | (my $line) = /^(.*)$/; 289 | 290 | if ( $line =~ m/^Providers:/ ) { 291 | $inProviders = 1; 292 | $inConsumers = 0; 293 | } 294 | if( $line =~ m/^Consumers:/ ) { 295 | $inProviders = 0; 296 | $inConsumers = 1; 297 | } 298 | 299 | if( ( $line =~ m/^\d+\.[\s]*Name:/ ) && ( $inProviders == 1 ) ) { 300 | $gptid = $line; 301 | $gptid =~ s/^\d+\.\sName:[\s]*(.*)[\s]*$/$1/; 302 | } 303 | 304 | if( ( $line =~ m/^\d+\.\sName:/ ) && ( $inConsumers == 1 ) ) { 305 | $part = $line; 306 | $part =~ s/^\d+\.\sName:[\s]*(.*)[\s]*$/$1/; 307 | 308 | my $ipart = ""; 309 | my $oldkey = ""; 310 | 311 | foreach my $p ( keys %partitions ) { 312 | my $ilabel = $partitions{ $p }; 313 | 314 | if( $ilabel->{'partition'} eq $part ) { 315 | $ipart = $ilabel; 316 | $oldkey = $p; 317 | } 318 | } 319 | 320 | if( $oldkey ne "" ) { 321 | 322 | delete( $partitions{ $oldkey } ); 323 | 324 | $ipart->{'gptid'} = $gptid; 325 | 326 | $partitions{ $gptid } = $ipart; 327 | } 328 | 329 | $ipart = ""; 330 | $oldkey = ""; 331 | $part = ""; 332 | $gptid = ""; 333 | } 334 | } 335 | 336 | foreach my $p ( keys %partitions ) { 337 | my $ilabel = $partitions{ $p }; 338 | 339 | if( $ilabel->{'gptid'} eq "" ) { 340 | delete( $partitions{ $p } ); 341 | 342 | $ilabel->{'gptid'} = "gptid/" . $ilabel->{'uuid'}; 343 | 344 | $partitions{ $ilabel->{'gptid'} } = $ilabel; 345 | } 346 | } 347 | } 348 | 349 | sub parseMultiPath { 350 | # multipath/disk3 OPTIMAL da70 (ACTIVE) 351 | # da22 (PASSIVE) 352 | my $multipath = ""; 353 | my $multipathState = ""; 354 | 355 | my $inProviders = 0; 356 | my $inConsumers = 0; 357 | 358 | my $disk = ""; 359 | my $diskMode = ""; 360 | 361 | for( `gmultipath list` ) { 362 | (my $line) = /^(.*)$/; 363 | 364 | if ( $line =~ m/^Providers:/ ) { 365 | $inProviders = 1; 366 | $inConsumers = 0; 367 | 368 | $multipath = ""; 369 | $multipathState = ""; 370 | 371 | $disk = ""; 372 | $diskMode = ""; 373 | } 374 | if( $line =~ m/^Consumers:/ ) { 375 | $inProviders = 0; 376 | $inConsumers = 1; 377 | } 378 | 379 | if( ( $line =~ m/^\d+\.[\s]*Name:/ ) && ( $inProviders == 1 ) ) { 380 | $multipath = $line; 381 | $multipath =~ s/^\d+\.\sName:[\s]*(.*)[\s]*$/$1/; 382 | } 383 | if( ( $line =~ m/^[\s]*State:/ ) && ( $inProviders == 1 ) ) { 384 | $multipathState = $line; 385 | $multipathState =~ s/^[\s]*State:[\s]*(.*)[\s]*$/$1/; 386 | } 387 | 388 | if( ( $line =~ m/^\d+\.[\s]*Name:/ ) && ( $inConsumers == 1 ) ) { 389 | $disk = $line; 390 | $disk =~ s/^\d+\.\sName:[\s]*(.*)[\s]*$/$1/; 391 | $diskMode = ""; 392 | 393 | $devices{ $disk }->{'multipath'} = $multipath; 394 | $devices{ $disk }->{'multipath_mode'} = "n/a"; 395 | $devices{ $disk }->{'multipath_state'} = $multipathState; 396 | } 397 | if( ( $line =~ m/^[\s]+State:/ ) && ( $inConsumers == 1 ) ) { 398 | $diskMode = $line; 399 | $diskMode =~ s/^[\s]+State:[\s]*(.*)[\s]*$/$1/; 400 | 401 | $devices{ $disk }->{'multipath_mode'} = $diskMode; 402 | 403 | foreach my $gptid ( keys %partitions ) { 404 | if( $partitions{ $gptid }->{'component'} eq $multipath ) { 405 | 406 | $partitions{ $gptid }->{'path'} = $multipath; 407 | 408 | $devices{ $disk }->{'partitioned'} = 1; 409 | 410 | if( $partitions{ $gptid }->{'devices'} eq "" ) { 411 | $partitions{ $gptid }->{'device'} = $disk; 412 | $partitions{ $gptid }->{'devices'} = $disk; 413 | } 414 | else { 415 | $partitions{ $gptid }->{'devices'} = $partitions{ $gptid }->{'devices'} . "," . $disk; 416 | } 417 | } 418 | } 419 | } 420 | } 421 | 422 | foreach my $gptid ( keys %partitions ) { 423 | if( $partitions{ $gptid }->{'devices'} eq "" ) { 424 | $partitions{ $gptid }->{'devices'} = $partitions{ $gptid }->{'device'}; 425 | } 426 | } 427 | } 428 | 429 | sub parseGeli { 430 | my $inProviders = 0; 431 | my $inConsumers = 0; 432 | 433 | my $encrypted = ""; 434 | my $algorithm = ""; 435 | 436 | for( `geli list` ) { 437 | (my $line) = /^(.*)$/; 438 | 439 | if ( $line =~ m/^Providers:/ ) { 440 | $inProviders = 1; 441 | $inConsumers = 0; 442 | } 443 | 444 | if( $line =~ m/^Consumers:/ ) { 445 | $inProviders = 0; 446 | $inConsumers = 1; 447 | } 448 | 449 | if( $line =~ m/^Geom name:/ ) { 450 | $encrypted = $line; 451 | $encrypted =~ s/^Geom name:[\s]*(.*)[\s]*$/$1/; 452 | $algorithm = ""; 453 | } 454 | 455 | if( $line =~ m/^EncryptionAlgorithm:/ ) { 456 | $algorithm = $line; 457 | $algorithm =~ s/^EncryptionAlgorithm:[\s]*(.*)[\s]*$/$1/; 458 | } 459 | 460 | if( ( $line =~ m/^\d+\.\sName:/ ) && ( $inConsumers == 1 ) ) { 461 | my $plain = $line; 462 | $plain =~ s/^\d+\.\sName:[\s]*(.*)[\s]*$/$1/; 463 | 464 | foreach my $gptid ( keys %partitions ) { 465 | if( $gptid eq $plain ) { 466 | my $ilabel = $partitions{ $plain }; 467 | $ilabel->{'gptid'} = $encrypted; 468 | $ilabel->{'encryption'} = $algorithm; 469 | delete( $partitions{ $plain } ); 470 | $partitions{ $encrypted } = $ilabel; 471 | } 472 | } 473 | } 474 | } 475 | } 476 | 477 | sub parseZpools { 478 | my $zpool = ""; 479 | my $state = ""; 480 | my @hierarchy; 481 | 482 | for( `zpool status` ) { 483 | (my $line) = /^(.*)$/; 484 | 485 | if ( $line =~ m/^[\s]*pool:/ ) { 486 | $zpool = $line; 487 | $zpool =~ s/^[\s]*pool:[\s]*(.*)[\s]*$/$1/; 488 | next; 489 | } 490 | 491 | if ( $line =~ m/^[\s]*state:/ ) { 492 | $state = $line; 493 | $state =~ s/^[\s]*state:[\s]*(.*)[\s]*$/$1/; 494 | next; 495 | } 496 | 497 | if ( $line =~ m/^[\s]+NAME[\s]+STATE[\s]+/ ) { 498 | next; 499 | } 500 | 501 | if( $line =~ m/^\t/ ) { 502 | # ok, line to handle 503 | } 504 | else { 505 | next; 506 | } 507 | 508 | my $whites = $line; 509 | $whites =~ s/^([\s]+)[^\s]+.*$/$1/; 510 | my $level = ( length( $whites ) - 1 ) / 2; 511 | 512 | my $item = $line; 513 | $item =~ s/^[\s]*([^\s]+)[\s]*.*$/$1/; 514 | 515 | #if( $item =~ m/\.eli$/ ) { 516 | # $item =~ s/^(.*)\.eli$/$1/; 517 | # $encrypted = 1; 518 | #} 519 | 520 | splice @hierarchy , $level , scalar( @hierarchy ) , ( $item ); 521 | 522 | my $location = ""; 523 | foreach my $x ( @hierarchy ) { 524 | if( $x eq $item ) { 525 | next; 526 | } 527 | if( $location eq "" ) { 528 | $location = $x; 529 | } 530 | else { 531 | $location = $location . "/" . $x; 532 | } 533 | } 534 | 535 | if ( $item =~ m/^gptid\// ) { 536 | my $gptid = $item; 537 | 538 | if( exists( $partitions{ $gptid } ) ) { 539 | $partitions{ $gptid }->{'zpool'} = $zpool; 540 | $partitions{ $gptid }->{'zpool_location'} = $location; 541 | $partitions{ $gptid }->{'zpool_state'} = $state; 542 | } 543 | } 544 | else { 545 | my $part = $item; 546 | 547 | foreach my $gptid ( keys %partitions ) { 548 | if( $partitions{ $gptid }->{'partition'} eq $part ) { 549 | $partitions{ $gptid }->{'zpool'} = $zpool; 550 | $partitions{ $gptid }->{'zpool_location'} = $location; 551 | $partitions{ $gptid }->{'zpool_state'} = $state; 552 | } 553 | } 554 | } 555 | } 556 | 557 | #NAME SIZE ALLOC FREE EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT 558 | #freenas-boot 57.5G 4.53G 53.0G - - 7% 1.00x ONLINE - 559 | #volBACKUP 14.5T 6.00T 8.50T - 23% 41% 1.00x ONLINE /mnt 560 | my $index_altroot_column = 9; 561 | for( `zpool list` ) { 562 | (my $line) = /^(.*)$/; 563 | 564 | if( $line =~ m/^NAME/ ) { 565 | my $altroot_header = (split ' ', $line)[9]; 566 | if( $altroot_header eq "ALTROOT" ) { 567 | $index_altroot_column = 9; 568 | } 569 | else { 570 | # we can have an extra column CKPOINT 571 | $index_altroot_column = 10; 572 | } 573 | next; 574 | } 575 | 576 | my $zpool = $line; 577 | $zpool =~ s/^([^\s]+)\s.*$/$1/; 578 | 579 | my $altroot = (split ' ', $line)[$index_altroot_column]; 580 | 581 | if( $altroot ne "-" ) { 582 | foreach my $gptid ( keys %partitions ) { 583 | if( $partitions{ $gptid }->{'zpool'} eq $zpool ) { 584 | $partitions{ $gptid }->{'zpool_mount'} = $altroot . "/" . $zpool; 585 | } 586 | } 587 | } 588 | } 589 | } 590 | 591 | sub parseSasDevices { 592 | my ($sasircu) = @_; 593 | 594 | for( `$sasircu list | grep '^[ ]*[0-9]'` ) { 595 | (my $controller,my $adapter) = /^[\s]+([0-9]+)[\s]+([^\s]+)[\s]+.*$/; 596 | 597 | 598 | my $ondisk = 0; 599 | my $disk; 600 | for( `$sasircu $controller display ` ) { 601 | (my $line) = /^(.*)$/; 602 | 603 | if( $line =~ m/^Device is a Hard disk/ ) { 604 | $ondisk = 1; 605 | $disk = {}; 606 | } 607 | if( $ondisk == 1 ) { 608 | if( $line =~ m/Enclosure/ ) { 609 | $disk->{'enclosure'} = $line; 610 | $disk->{'enclosure'} =~ s/^.*:[ ]+([^ ]*).*$/$1/; 611 | } 612 | if( $line =~ m/Slot/ ) { 613 | $disk->{'slot'} = $line; 614 | $disk->{'slot'} =~ s/^.*:[ ]+([^ ]*).*$/$1/; 615 | } 616 | if( $line =~ m/Protocol/ ) { 617 | $disk->{'protocol'} = $line; 618 | $disk->{'protocol'} =~ s/^.*:[ ]+([^ ]*).*$/$1/; 619 | } 620 | if( $line =~ m/Serial No/ ) { 621 | $disk->{'serial'} = $line; 622 | $disk->{'serial'} =~ s/^.*:[ ]+([^ ]*).*$/$1/; 623 | $disk->{'serial'} =~ s/[^a-zA-Z0-9]//g; 624 | } 625 | if( $line =~ m/Drive Type/ ) { 626 | $disk->{'type'} = $line; 627 | $disk->{'type'} =~ s/^.*:[ ]+([^ ]*).*$/$1/; 628 | } 629 | 630 | if( $line =~ m/Drive Type/ ) { 631 | $disk->{'controller'} = $controller; 632 | $disk->{'adapter'} = $adapter; 633 | if( $sasircu eq "sas2ircu" ) { 634 | $disk->{'sas'} = "sas2"; 635 | } 636 | if( $sasircu eq "sas3ircu" ) { 637 | $disk->{'sas'} = "sas3"; 638 | } 639 | $sasdevices{ $disk->{'serial'} } = $disk; 640 | 641 | my $url = $adapter . "(" . $controller . "):" . $disk->{'enclosure'} . "#" . $disk->{'slot'}; 642 | $disk->{'url'} = $url; 643 | 644 | $ondisk = 0; 645 | } 646 | } 647 | } 648 | } 649 | } 650 | 651 | sub parseSmartctlDevice { 652 | my ($dev) = @_; 653 | 654 | my $idev = $devices{ $dev }; 655 | my $temp = ""; 656 | my $poh = ""; 657 | for( `smartctl -a /dev/$dev` ) { 658 | (my $line) = /^(.*)$/; 659 | if ( $line =~ m/^190 / ) { 660 | $temp = (split ' ', $line)[9]; 661 | } 662 | else { 663 | if ( ( $line =~ m/^194 / ) && ( "$temp" eq "" ) ) { 664 | $temp = (split ' ', $line)[9]; 665 | } 666 | else { 667 | if ( $line =~ m/^Current Drive Temperature:/ ) { 668 | $temp = (split ' ', $line)[3]; 669 | } 670 | else { 671 | # do nothing 672 | } 673 | } 674 | } 675 | 676 | if ( $line =~ m/^ 9 / ) { 677 | $poh = (split ' ', $line)[9]; 678 | } 679 | else { 680 | if ( $line =~ m/number of hours powered up/ ) { 681 | $poh = (split ' ', $line)[6]; 682 | $poh =~ s/^(.*)\..*$/$1/; 683 | } 684 | } 685 | } 686 | 687 | if ( $temp eq "" ) { 688 | $temp = "???" 689 | } 690 | if ( $poh eq "" ) { 691 | $poh = "???" 692 | } 693 | 694 | $idev->{'temperature'} = $temp; 695 | $idev->{'poh'} = $poh; 696 | } 697 | 698 | sub completeDiskType { 699 | foreach my $device ( keys %devices ) { 700 | if( $device =~ m/^nvd/ ) { 701 | $devices{ $device }->{'type'}= 'NVME'; 702 | } 703 | if( $device =~ m/^pmem/ ) { 704 | $devices{ $device }->{'type'}= 'NVRAM'; 705 | } 706 | } 707 | } 708 | 709 | sub printHelp() { 710 | print "disklist.pl [-i: ]* [-fs:]* [-disk:]* [-nolabel] [-multi[:]* [-o:] [-c:]* [-sort:]* [-all] [-long] [-short] [-static] [-smartctl] [-locate[: geom disk list\n"; 717 | print " > gpart list\n"; 718 | print " > glabel list\n"; 719 | print " > geli list\n"; 720 | print " > gmultipath list\n"; 721 | print " > zpool status\n"; 722 | print " > zpool list\n"; 723 | print " > sas2ircu list\n"; 724 | print " > sas2ircu # display\n"; 725 | print " > sas2ircu # locate #:#\n"; 726 | print " > sas3ircu list\n"; 727 | print " > sas3ircu # display\n"; 728 | print " > sas3ircu # locate #:#\n"; 729 | print " > smartctl -A \n"; 730 | print "\n"; 731 | print " This program comes with ABSOLUTELY NO WARRANTY.\n"; 732 | print " This is free software, and you are welcome to redistribute it under certain conditions (cf. GNU Lesser General Public Licence v3)\n"; 733 | print "\n"; 734 | print " Arguments are:\n"; 735 | print "\n"; 736 | print " -i: \n"; 737 | print " Filter devices/partitions that match the specified on the corresponding \n"; 738 | print " Classes are:\n"; 739 | print " dev : Filter on a device name (example: -i:dev ada0)\n"; 740 | print " zpool : Filter on a ZFS pool name (example: -i:zpool tank)\n"; 741 | print " part : Filter on a partition name (example: -i:part ada0p1)\n"; 742 | print " gptid : Filter on a gpt label (example: -i:gptid gptid/fe38f700-829a-11e7-810a-00074304a2f0)\n"; 743 | print " serial : Filter on a Disk Serial number (example: -i:serial YFJ4G2BD)\n"; 744 | print " multipath : Filter on a SAS multipath disk name (example: -i:multipath multipath/disk1)\n"; 745 | print " enclosure : Filter on a SAS enclosure location (example: -i:enclosure 1#6), you can specify or #\n"; 746 | print "\n"; 747 | print " This argument can be set multiple times for specifying multiple filters.\n"; 748 | print "\n"; 749 | print " Be careful, evaluation is done using the following logical form :\n"; 750 | print " ( device_predicate1 OR device_predicate2 OR ... ) AND ( partition_predicate1 OR partition_predicate2 .... )\n"; 751 | print "\n"; 752 | print " devices predicates are : dev, multipath, serial, enclosure\n"; 753 | print " partitions predicates are : zpool, part, gptid\n"; 754 | print "\n"; 755 | print " -fs:\n"; 756 | print " Filter devices/partitions that have a partition that match the specified file system type\n"; 757 | print " FileSystem types are:\n"; 758 | print " all : Accept any partition type and disk not partitioned. (example: -fs:all)\n"; 759 | print " none : Accept only disk not partitioned. (example: -fs:none)\n"; 760 | print " any : Accept any partition type. (example: -fs:any)\n"; 761 | print " zfs : Accept only freebsd-zfs partition type. (example: -fs:zfs)\n"; 762 | print " swap : Accept only freebsd-swap partition type. (example: -fs:swap)\n"; 763 | print " boot : Accept only bios-boot partition type. (example: -fs:boot)\n"; 764 | print " unknown : Accept only unknown partition type. (example: -fs:unknown)\n"; 765 | print "\n"; 766 | print " This argument can be set multiples times for specifying a list of fs types.\n"; 767 | print " By default this command use theses options : -fs:zfs -fs:none\n"; 768 | print "\n"; 769 | print " -disk:\n"; 770 | print " Filter devices/partitions that match the specified disk type\n"; 771 | print " Disk types are:\n"; 772 | print " sdd : Solid State Drive (example: -disk:ssd)\n"; 773 | print " hdd : Spindle Drive (example: -disk:hdd)\n"; 774 | print " nvme : NVME Solid State Drive (example: -disk:nvme)\n"; 775 | print " nvram : NVRAM Drive (example: -disk:nvram)\n"; 776 | print " unknown : Unknown drive type (example: -disk:unknown)\n"; 777 | print "\n"; 778 | print " This argument can be set multiples times for specifying a list of disk types\n"; 779 | print "\n"; 780 | print " -nolabel\n"; 781 | print " Restrict output only to devices (disks) without reporting any informations about partitions.\n"; 782 | print " That allows for example to report on available disks with these options: -nolabel -multi:none -multi:active\n"; 783 | print " By default, partitioned disk will be reported with partitions details.\n"; 784 | print "\n"; 785 | print " -multi[:]\n"; 786 | print " Filter devices/partitions that use SAS multipath and optionaly match the multipath mode (ACTIVE or PASSIVE)\n"; 787 | print " Multipath modes are:\n"; 788 | print " none : The disk device must not use SAS multipath (example: -multi:none)\n"; 789 | print " active : The disk device must use SAS multipath and be the ACTIVE device (example: -multi:active)\n"; 790 | print " passive : The disk device must use SAS multipath and be the PASSIVE device (example: -multi:passive)\n"; 791 | print "\n"; 792 | print " If no Multipath mode is specified, the disk device must use SAS multipath, whatever the mode of the device.\n"; 793 | print " This argument can be set multiples times\n"; 794 | print "\n"; 795 | print " -o:\n"; 796 | print " Output format to use for reporting devices.\n"; 797 | print " Output formats are:\n"; 798 | print " col : Each device is reported in a dedicated row. Information is padded with whitespaces into columns and columns are separated by 2 whitespaces.\n"; 799 | print " csv : Each device is reported in a dedicated row. Columns are separated by a semi-colon accordingly to CSV format without any padding.\n"; 800 | print " list : Each device is reported in multiple rows. Each information is presented on a dedicated row using the format \"property: value\".\n"; 801 | print "\n"; 802 | print " If ommited, the default is -o:col\n"; 803 | print "\n"; 804 | print " -c:\n"; 805 | print " Specify a series of columns to report represented by . Each column has a symbol identifier.\n"; 806 | print " The order of a symbol identifier determines the order of the column in the report output.\n"; 807 | print " Columns identifiers are:\n"; 808 | print " p : Partition name (example: ada0p1)\n"; 809 | print " P : Partition type. (example: freebsd-zfs)\n"; 810 | print " l : Partition label (gptid). (example: gptid/fe38f700-829a-11e7-810a-00074304a2f0)\n"; 811 | print " z : ZFS pool name (example: tank)\n"; 812 | print " Z : Partition location on the pool (example: tank/mirror-0)\n"; 813 | print " M : ZFS mount point (example: /mnt/tank)\n"; 814 | print " X : Partition encryption algorithm (example: AES-XTS)\n"; 815 | print "\n"; 816 | print " d : device code (example: ada0)\n"; 817 | print " t : device sectorsize in Bytes (example: 512)\n"; 818 | print "\n"; 819 | print " D : Disk Description (example: HITACHI HUS72302CLAR2000)\n"; 820 | print " U : Disk size in Gb (example: 2000)\n"; 821 | print " T : Disk type (example: SSD)\n"; 822 | print " S : Disk Serial Number (example: YFJ4G2BD)\n"; 823 | print " R : Disk Rotational speed (example: 7200)\n"; 824 | print " H : Disk temperature in °C (smartctl) (example: 43°)\n"; 825 | print " h : Power on Hours (smartctl) (example: 2711)\n"; 826 | print "\n"; 827 | print " e : Controller/Enclosure location (example: SAS3008(0):1#6)\n"; 828 | print "\n"; 829 | print " m : SAS Multipath device (example: multipath/disk1)\n"; 830 | print " o : SAS Multipath device mode (example: ACTIVE or PASSIVE)\n"; 831 | print " s : SAS Multipath state (example: OPTIMAL)\n"; 832 | print "\n"; 833 | print " Columns using \"smartctl\" will be reported only if the -smartctl switch is used.\n"; 834 | print " Some of theses attributes are vendor specifics and may be not available (mainly for SAS devices)...\n"; 835 | print "\n"; 836 | print " You can add -short, -long switches for using a column profile + specified columns.\n"; 837 | print " In this case, the column order is based on the columns from the selected profile followed by the specified columns.\n"; 838 | print " If a column identifier appears more than once, the column will be reported multiple times.\n"; 839 | print "\n"; 840 | print " -sort:\n"; 841 | print " Specify a series of columns to use for sorting devices and partitions.\n"; 842 | print " The order of the symbol identifier determines the sort order of the column in the output.\n"; 843 | print " This tools always reports partitions first and unpartitioned disks afterwards.\n"; 844 | print " Columns related to partitions (like PplzZMX) are unused for sorting devices (but used for sorting partitions).\n"; 845 | print " By default, the sort is set to zZmodp (if a column is not reported, it is removed from the sort key also)\n"; 846 | print " Columns identifiers are the same as used for -c: switch\n"; 847 | print "\n"; 848 | print " -all\n"; 849 | print " Reports all columns. It's equivalent to -c:pPlzZMdtDUTSRHhemosX\n"; 850 | print "\n"; 851 | print " -long\n"; 852 | print " Reports on most of the columns and is the default. It's equivalent to -c:plzdDUTSRHemosX\n"; 853 | print " Can be used in conjunction with -c: switch to add some columns to the -long profile.\n"; 854 | print "\n"; 855 | print " -short\n"; 856 | print " Reports with a basic number of columns. It's equivalent to -c:pzdDUT\n"; 857 | print " Can be used in conjunction with -c: switch to add some columns to the -short profile.\n"; 858 | print "\n"; 859 | print " -static\n"; 860 | print " By default, empty columns are automatically removed from the report.\n"; 861 | print " This switch prevent any columns to be removed even if they are empties.\n"; 862 | print "\n"; 863 | print " -smartctl\n"; 864 | print " Allow the use of \"smartctl\" commands for providing additional disk informations\n"; 865 | print " Without this switch, columns that need smartctl will be removed from the output.\n"; 866 | print "\n"; 867 | print " -locate[:duration]\n"; 868 | print " Blink leds for the specified duration in seconds for all reported disks (using sas2 or sas3 controllers)\n"; 869 | print " The default duration is 10 seconds when no specified.\n"; 870 | print " For example you can locate a drive by it's serial number:\n"; 871 | print " disklist.pl -i:serial YFJ4G2BD -locate\n"; 872 | print " -h\n"; 873 | print " Print this help.\n"; 874 | print "\n"; 875 | } 876 | 877 | sub printPartitions { 878 | foreach my $p ( keys %partitions ) { 879 | print ">>>>>> ( $p )\n"; 880 | printPartitionItem( $partitions{ $p } , 'partition' ); 881 | printPartitionItem( $partitions{ $p } , 'uuid' ); 882 | printPartitionItem( $partitions{ $p } , 'gptid' ); 883 | printPartitionItem( $partitions{ $p } , 'type' ); 884 | printPartitionItem( $partitions{ $p } , 'component' ); 885 | printPartitionItem( $partitions{ $p } , 'device' ); 886 | printPartitionItem( $partitions{ $p } , 'devices' ); 887 | printPartitionItem( $partitions{ $p } , 'path' ); 888 | printPartitionItem( $partitions{ $p } , 'zpool' ); 889 | printPartitionItem( $partitions{ $p } , 'zpool_location' ); 890 | printPartitionItem( $partitions{ $p } , 'zpool_state' ); 891 | printPartitionItem( $partitions{ $p } , 'zpool_mount' ); 892 | printPartitionItem( $partitions{ $p } , 'encryption' ); 893 | print "<<<<<<\n"; 894 | } 895 | die; 896 | } 897 | 898 | sub printPartitionItem() { 899 | my ($ilabel,$item) = @_; 900 | 901 | if( defined( $ilabel->{ $item } ) ){ 902 | print " $item : $ilabel->{ $item }\n"; 903 | } 904 | else { 905 | print " $item : \n"; 906 | } 907 | } 908 | 909 | sub parseArgumentsDiskList { 910 | my @arguments = @_; 911 | my $args_state=0; # 0 = none ; 1 = query entity ; 912 | foreach my $arg ( @arguments ) { 913 | if ( $args_state == 1 ) { 914 | push @query_component , $arg; 915 | 916 | $args_state = 0; 917 | next; 918 | } 919 | 920 | if ( $arg =~ m/^-i:.*$/ ) { 921 | my $c = $arg; 922 | $c =~ s/^-i:(.*)$/$1/; 923 | 924 | if( $c eq "" ) { 925 | print "$0 : Bad usage using -i:\n"; 926 | exit 1; 927 | } 928 | 929 | push @query_class , $c; 930 | 931 | $args_state = 1; 932 | next; 933 | } 934 | 935 | if ( $arg =~ m/^-c:.*$/ ) { 936 | my $x = $arg; 937 | $x =~ s/^-c:(.*)$/$1/; 938 | 939 | if( $x eq "" ) { 940 | print "$0 : Bad usage using -x:\n"; 941 | exit 1; 942 | } 943 | 944 | $output_columns = $output_columns . $x; 945 | 946 | next; 947 | } 948 | 949 | if ( $arg =~ m/^-sort:.*$/ ) { 950 | my $x = $arg; 951 | $x =~ s/^-sort:(.*)$/$1/; 952 | 953 | if( $x eq "" ) { 954 | print "$0 : Bad usage using -sort:\n"; 955 | exit 1; 956 | } 957 | 958 | $output_sort = $output_sort . $x; 959 | 960 | next; 961 | } 962 | 963 | if ( $arg =~ m/^-o:.*$/ ) { 964 | $output_layout = $arg; 965 | $output_layout =~ s/^-o:(.*)$/$1/; 966 | next; 967 | } 968 | 969 | if ( $arg eq "-all" ) { 970 | $output_columns = "pPlzZMdtDUTSRHhemosX" . $output_columns; 971 | next; 972 | } 973 | if ( $arg eq "-long" ) { 974 | $output_columns = "plzdDUTSRHemosX" . $output_columns; 975 | next; 976 | } 977 | if ( $arg eq "-short" ) { 978 | $output_columns = "pzdDUT" . $output_columns; 979 | next; 980 | } 981 | if ( $arg eq "-static" ) { 982 | $accept_collapse = 0; 983 | next; 984 | } 985 | if ( $arg eq "-smartctl" ) { 986 | $accept_smartctl=1; 987 | next; 988 | } 989 | if ( $arg eq "-locate" ) { 990 | $locate=10; 991 | next; 992 | } 993 | if ( $arg =~ m/^-locate:[\d]+$/ ) { 994 | my $m = $arg; 995 | $m =~ s/^-locate:(.*)$/$1/; 996 | $locate = int( $m ); 997 | next; 998 | } 999 | 1000 | if ( $arg eq "-multi" ) { 1001 | push @query_multipath , "ACTIVE" ; 1002 | push @query_multipath , "PASSIVE"; 1003 | next; 1004 | } 1005 | elsif ( $arg =~ m/^-multi:.*$/ ) { 1006 | my $m = $arg; 1007 | $m =~ s/^-multi:(.*)$/$1/; 1008 | $m = uc $m; 1009 | 1010 | if( $m eq "" ) { 1011 | print "$0 : Bad usage using -multi:\n"; 1012 | exit 1; 1013 | } 1014 | 1015 | if( $m eq "NONE" ) { 1016 | $m = ""; 1017 | } 1018 | 1019 | push @query_multipath , $m; 1020 | next; 1021 | } 1022 | 1023 | if ( $arg =~ m/^-fs:.*$/ ) { 1024 | my $fs_type = $arg; 1025 | $fs_type = $arg; 1026 | $fs_type =~ s/^-fs:(.*)$/$1/; 1027 | 1028 | if( $fs_type eq "" ) { 1029 | print "$0 : Bad usage using -fs:\n"; 1030 | exit 1; 1031 | } 1032 | 1033 | push @query_fs , $fs_type; 1034 | next; 1035 | } 1036 | 1037 | if ( $arg =~ m/^-disk:.*$/ ) { 1038 | my $disk_type = $arg; 1039 | $disk_type =~ s/^-disk:(.*)$/$1/; 1040 | $disk_type = uc $disk_type; 1041 | 1042 | if( $disk_type eq "UNKNOWN" ) { 1043 | $disk_type = "???" 1044 | } 1045 | 1046 | if( $disk_type eq "" ) { 1047 | print "$0 : Bad usage using -disk:\n"; 1048 | exit 1; 1049 | } 1050 | 1051 | push @query_disktype , $disk_type; 1052 | next; 1053 | } 1054 | 1055 | if( $arg eq "-nolabel" ) { 1056 | $accept_label = 0; 1057 | next; 1058 | } 1059 | 1060 | if ( $arg eq "-h" ) { 1061 | printHelp; 1062 | exit 0; 1063 | } 1064 | 1065 | print "$0 : Bad usage, unrecognized argument \"$arg\"\n"; 1066 | print "$0 : Use -h flag for more details.\n"; 1067 | exit 1; 1068 | } 1069 | 1070 | if( $output_columns eq "" ) { 1071 | $output_columns = "plzdDUTSRHXemos" . $output_columns; 1072 | } 1073 | 1074 | if( scalar @query_fs == 0 ) { 1075 | @query_fs = ( "zfs" , "none" ); 1076 | } 1077 | 1078 | if( scalar @query_multipath == 0 ) { 1079 | @query_multipath = ( "ACTIVE" , "PASSIVE" , "" ); 1080 | } 1081 | 1082 | if( $accept_smartctl == 0 ) { 1083 | $output_columns =~ tr/H//d; 1084 | $output_columns =~ tr/h//d; 1085 | } 1086 | 1087 | if( $args_state > 0 ) { 1088 | print "$0 : Bad usage, missing argument.\n"; 1089 | print "$0 : Use -h flag for more details.\n"; 1090 | exit 2; 1091 | } 1092 | } 1093 | 1094 | sub applyFilterDevice { 1095 | my ($idev) = @_; 1096 | 1097 | my $ok = 0; 1098 | foreach my $mm ( @query_multipath ) { 1099 | if( $mm eq $idev->{'multipath_mode'} ) { 1100 | $ok = 1; 1101 | } 1102 | } 1103 | if ( $ok == 0 ) { 1104 | return 0; 1105 | } 1106 | 1107 | #if( ( $accept_partitioned & 1 ) == 0 && $idev->{'partitioned'} == 0 ) { 1108 | #return 0; 1109 | #} 1110 | #if( ( $accept_partitioned & 2 ) == 0 && $idev->{'partitioned'} == 1 ) { 1111 | #return 0; 1112 | #} 1113 | 1114 | if( scalar @query_disktype == 0 ) { 1115 | } 1116 | else { 1117 | my $ok = 0; 1118 | foreach my $dt ( @query_disktype ) { 1119 | if( $idev->{'type'} eq $dt ) { 1120 | $ok = 1; 1121 | } 1122 | } 1123 | if( $ok == 0 ) { 1124 | return 0; 1125 | } 1126 | } 1127 | 1128 | if( scalar @query_class == 0 ) { 1129 | # do nothing 1130 | } 1131 | else { 1132 | my $ok = 0; 1133 | my $index = -1; 1134 | my $comp = ""; 1135 | my $classdev = 0; 1136 | foreach my $class ( @query_class ) { 1137 | $index = $index + 1; 1138 | $comp = $query_component[ $index ]; 1139 | 1140 | if( ( $class eq "dev" ) || ( $class eq "multipath" ) || ( $class eq "serial" ) || ( $class eq "enclosure" ) ) { 1141 | $classdev = 1; 1142 | } 1143 | 1144 | if( ( $class eq "dev" ) && ( $comp eq $idev->{'device'} ) ) { 1145 | $ok = 1; 1146 | } 1147 | 1148 | if( ( $class eq "multipath" ) && ( $comp eq $idev->{'multipath'} ) ) { 1149 | $ok = 1; 1150 | } 1151 | 1152 | if( ( $class eq "serial" ) && ( $comp eq $idev->{'serial'} ) ) { 1153 | $ok = 1; 1154 | } 1155 | 1156 | if( ( $class eq "enclosure" ) ) { 1157 | if( exists( $sasdevices{ $idev->{'serial'} } ) ) { 1158 | if( $comp =~ m/#/ ) { 1159 | my $enc = $comp; 1160 | my $slot = $comp; 1161 | 1162 | $enc =~ s/^([^#]+)#[^#]+$/$1/; 1163 | $slot =~ s/^[^#]+#([^#]+)$/$1/; 1164 | 1165 | my $ok_enclosure = 0; 1166 | my $ok_slot = 0; 1167 | if( $enc eq $sasdevices{ $idev->{'serial'} }->{'enclosure'} ) { 1168 | $ok_enclosure = 1; 1169 | } 1170 | if( $slot eq $sasdevices{ $idev->{'serial'} }->{'slot'} ) { 1171 | $ok_slot = 1; 1172 | } 1173 | 1174 | if( $ok_enclosure == 1 && $ok_slot == 1 ) { 1175 | $ok = 1; 1176 | } 1177 | } 1178 | else { 1179 | if( $comp eq $sasdevices{ $idev->{'serial'} }->{'enclosure'} ) { 1180 | $ok = 1; 1181 | } 1182 | } 1183 | } 1184 | } 1185 | } 1186 | if( $ok == 0 && $classdev == 1 ) { 1187 | return 0; 1188 | } 1189 | } 1190 | 1191 | return 1; 1192 | } 1193 | 1194 | sub applyFilterLabel { 1195 | my ($ilabel) = @_; 1196 | 1197 | if( scalar @query_class == 0 ) { 1198 | # do nothing 1199 | } 1200 | else { 1201 | my $ok = 0; 1202 | my $index = -1; 1203 | my $comp = ""; 1204 | my $classpart = 0; 1205 | foreach my $class ( @query_class ) { 1206 | $index = $index + 1; 1207 | $comp = $query_component[ $index ]; 1208 | 1209 | if( ( $class eq "gptid" ) || ( $class eq "part" ) || ( $class eq "zpool" ) ) { 1210 | $classpart = 1; 1211 | } 1212 | 1213 | if( $class eq "gptid" ) { 1214 | if( $ilabel eq "" ) { 1215 | # do nothing 1216 | } 1217 | elsif( $comp eq $ilabel->{'gptid'} ) { 1218 | $ok = 1; 1219 | } 1220 | } 1221 | 1222 | if( $class eq "part" ) { 1223 | if( $ilabel eq "" ) { 1224 | # do nothing 1225 | } 1226 | elsif( $comp eq $ilabel->{'partition'} ) { 1227 | $ok = 1; 1228 | } 1229 | } 1230 | 1231 | if( $class eq "zpool" ) { 1232 | if( $ilabel eq "" ) { 1233 | # do nothing 1234 | } 1235 | elsif( $comp eq $ilabel->{'zpool'} ) { 1236 | $ok = 1; 1237 | } 1238 | } 1239 | } 1240 | if( $ok == 0 && $classpart == 1 ) { 1241 | return 0; 1242 | } 1243 | } 1244 | 1245 | if( $ilabel eq "" ) { 1246 | my $ok = 0; 1247 | foreach my $fs ( @query_fs ) { 1248 | if( ( $fs eq "all" ) || ( $fs eq "none" ) ) { 1249 | $ok = 1; 1250 | } 1251 | } 1252 | 1253 | if( $ok == 0 ) { 1254 | return 0; 1255 | } 1256 | } 1257 | else { 1258 | my $ok = 0; 1259 | foreach my $fs ( @query_fs ) { 1260 | if( ( $fs eq "all" ) || ( $fs eq "any" ) ) { 1261 | $ok = 1; 1262 | } 1263 | elsif( $fs eq "zfs" ) { 1264 | if( $ilabel->{'type'} eq "freebsd-zfs" ) { 1265 | $ok = 1; 1266 | } 1267 | } 1268 | elsif( $fs eq "swap" ) { 1269 | if( $ilabel->{'type'} eq "freebsd-swap" ) { 1270 | $ok = 1; 1271 | } 1272 | } 1273 | elsif( $fs eq "boot" ) { 1274 | if( $ilabel->{'type'} eq "bios-boot" ) { 1275 | $ok = 1; 1276 | } 1277 | } 1278 | else { 1279 | if( $ilabel->{'type'} eq $fs ) { 1280 | $ok = 1; 1281 | } 1282 | } 1283 | } 1284 | 1285 | if( $ok == 0 ) { 1286 | return 0; 1287 | } 1288 | } 1289 | 1290 | return 1; 1291 | } 1292 | 1293 | # compare items that foloww the scheme xxxxxxx01 like devices (ada01) or multipath (multipath/disk30) 1294 | # 1295 | sub compare_components { 1296 | my ($a,$b) = @_; 1297 | if( ( $a =~ m/[0-9]$/ ) && ( $b =~ m/[0-9]$/ ) ) { 1298 | my $pa = $a; 1299 | my $ia = $a; 1300 | $pa =~ s/^(.*?)([0-9]+)$/$1/; 1301 | $ia =~ s/^(.*?)([0-9]+)$/$2/; 1302 | 1303 | my $pb = $b; 1304 | my $ib = $b; 1305 | $pb =~ s/^(.*?)([0-9]+)$/$1/; 1306 | $ib =~ s/^(.*?)([0-9]+)$/$2/; 1307 | 1308 | if( $pa eq $pb ) { 1309 | return $ia <=> $ib; 1310 | } 1311 | return $pa cmp $pb; 1312 | } 1313 | return $a cmp $b; 1314 | } 1315 | 1316 | # sort 1317 | # zpool , multipath , multipath_mode , device, partition 1318 | sub compare_labels { 1319 | my ($a,$b) = @_; 1320 | 1321 | my $labelA = $partitions{ $a }; 1322 | my $labelB = $partitions{ $b }; 1323 | 1324 | my $devA = $devices{ $labelA->{'device'} }; 1325 | my $devB = $devices{ $labelB->{'device'} }; 1326 | 1327 | my $ocol; 1328 | 1329 | my $valA; 1330 | my $valB; 1331 | 1332 | my $res; 1333 | 1334 | my $sort = $output_sort; 1335 | if( $sort eq "" ) { 1336 | $sort = "zZmodp"; 1337 | } 1338 | 1339 | foreach my $col (split //, $sort) { 1340 | $ocol = getColumnLayout( $col ); 1341 | 1342 | $valA = getPrintableValue( $devA , $labelA , $ocol ); 1343 | $valB = getPrintableValue( $devB , $labelB , $ocol ); 1344 | 1345 | $res = compare_components( $valA , $valB ); 1346 | if( $res == 0 ) { 1347 | next; 1348 | } 1349 | return $res; 1350 | } 1351 | return 0; 1352 | } 1353 | 1354 | sub compare_devices { 1355 | my ($a,$b) = @_; 1356 | 1357 | my $devA = $devices{ $a }; 1358 | my $devB = $devices{ $b }; 1359 | 1360 | my $ocol; 1361 | 1362 | my $valA; 1363 | my $valB; 1364 | 1365 | my $res; 1366 | 1367 | my $sort = $output_sort; 1368 | if( $sort eq "" ) { 1369 | $sort = "zZmodp"; 1370 | } 1371 | 1372 | foreach my $col (split //, $sort) { 1373 | $ocol = getColumnLayout( $col ); 1374 | 1375 | $valA = getPrintableValue( $devA , "" , $ocol ); 1376 | $valB = getPrintableValue( $devB , "" , $ocol ); 1377 | 1378 | $res = compare_components( $valA , $valB ); 1379 | if( $res == 0 ) { 1380 | next; 1381 | } 1382 | return $res; 1383 | } 1384 | return 0; 1385 | } 1386 | 1387 | sub prepareOutputLayout { 1388 | my ($display_columns) = @_; 1389 | undef(@layout); 1390 | 1391 | my $ocol; 1392 | foreach my $col (split //, $display_columns) { 1393 | $ocol = getColumnLayout( $col ); 1394 | 1395 | push @layout , $ocol; 1396 | } 1397 | } 1398 | 1399 | sub getColumnLayout { 1400 | my ($col) = @_; 1401 | 1402 | my $ocol; 1403 | 1404 | my $owner = ""; 1405 | my $info = ""; 1406 | my $title = ""; 1407 | my $align = "left"; 1408 | 1409 | $ocol = {}; 1410 | 1411 | if( $col eq "p" ) { $owner = 'label' ; $info = 'partition'; $title="partition"; } 1412 | if( $col eq "P" ) { $owner = 'label' ; $info = 'type'; $title="fs"; } 1413 | if( $col eq "l" ) { $owner = 'label' ; $info = 'gptid'; $title="label"; } 1414 | if( $col eq "z" ) { $owner = 'label' ; $info = 'zpool'; $title="zpool"; } 1415 | if( $col eq "Z" ) { $owner = 'label' ; $info = 'zpool_location'; $title="zpool-location"; } 1416 | if( $col eq "M" ) { $owner = 'label' ; $info = 'zpool_mount'; $title="zpool-mount"; } 1417 | if( $col eq "X" ) { $owner = 'label' ; $info = 'encryption'; $title="encryption"; } 1418 | if( $col eq "d" ) { $owner = 'device' ; $info = 'device'; $title="device"; } 1419 | if( $col eq "t" ) { $owner = 'device' ; $info = 'sectorsize'; $title="sector"; $align="right"; } 1420 | if( $col eq "D" ) { $owner = 'device' ; $info = 'description'; $title="disk"; } 1421 | if( $col eq "U" ) { $owner = 'device' ; $info = 'disksize'; $title="size"; $align="right"; } 1422 | if( $col eq "T" ) { $owner = 'device' ; $info = 'type'; $title="type"; } 1423 | if( $col eq "S" ) { $owner = 'device' ; $info = 'serial'; $title="serial"; } 1424 | if( $col eq "R" ) { $owner = 'device' ; $info = 'rpm'; $title="rpm"; $align="right"; } 1425 | if( $col eq "H" ) { $owner = 'smartctl' ; $info = 'temperature'; $title="temp"; $align="right"; } 1426 | if( $col eq "h" ) { $owner = 'smartctl' ; $info = 'poh'; $title="power-on-hours"; $align="right"; } 1427 | if( $col eq "e" ) { $owner = 'serial' ; $info = 'url'; $title="sas-location"; } 1428 | if( $col eq "m" ) { $owner = 'device' ; $info = 'multipath'; $title="multipath"; } 1429 | if( $col eq "o" ) { $owner = 'device' ; $info = 'multipath_mode'; $title="multipath-mode"; } 1430 | if( $col eq "s" ) { $owner = 'device' ; $info = 'multipath_state'; $title="multipath-state"; } 1431 | 1432 | if( $owner eq "" ) { 1433 | print "Unknown column identifier $col\n"; 1434 | exit 1; 1435 | } 1436 | 1437 | $ocol->{'col'} = $col; 1438 | $ocol->{'owner'} = $owner; 1439 | $ocol->{'info'} = $info; 1440 | $ocol->{'title'} = $title; 1441 | $ocol->{'maxlength'} = length($title); # computed later with values 1442 | $ocol->{'align'} = $align; 1443 | $ocol->{'enabled'} = 0; 1444 | 1445 | return $ocol; 1446 | } 1447 | 1448 | sub getPrintableValue { 1449 | my ($idev,$ilabel,$layout) = @_; 1450 | 1451 | my $owner = $layout->{'owner'}; 1452 | my $info = $layout->{'info'}; 1453 | 1454 | my $value = ""; 1455 | if( $owner eq "device" ) { 1456 | $value = $idev->{ $info }; 1457 | } 1458 | if( $owner eq "smartctl" ) { 1459 | if( $info eq "temperature" ) { 1460 | if( $idev->{ 'temperature' } eq "" ) { 1461 | parseSmartctlDevice( $idev->{'device'} ); 1462 | } 1463 | } 1464 | if( $info eq "poh" ) { 1465 | if( $idev->{ 'poh' } eq "" ) { 1466 | parseSmartctlDevice( $idev->{'device'} ); 1467 | } 1468 | } 1469 | $value = $idev->{ $info }; 1470 | } 1471 | if( $owner eq "label" ) { 1472 | if( $ilabel eq "" ) { 1473 | $value = ""; 1474 | } 1475 | else { 1476 | $value = $ilabel->{ $info }; 1477 | } 1478 | } 1479 | if( $owner eq "serial" ) { 1480 | if( exists( $sasdevices{ $idev->{'serial'} } ) ) { 1481 | $value = $sasdevices{ $idev->{'serial'} }->{ $info }; 1482 | } 1483 | else { 1484 | $value = ""; 1485 | } 1486 | } 1487 | if( $info eq "disksize" ) { 1488 | $value = floor( $value / 1000 / 1000 / 1000 ); 1489 | } 1490 | if( $info eq "device" ) { 1491 | if( $ilabel ne "" ) { 1492 | $value = $ilabel->{'devices'}; 1493 | } 1494 | } 1495 | if( $info eq "multipath_mode" ) { 1496 | if( $ilabel ne "" && $ilabel->{'devices'} =~ m/,/ ) { 1497 | $value = ""; 1498 | } 1499 | } 1500 | 1501 | if( !defined $value ) { 1502 | print "Error while accessing data for (owner=$owner, data=$info)\n"; 1503 | die; 1504 | } 1505 | if( !defined $layout->{ 'maxlength' } ) { 1506 | print "Error while accessing data for (owner=$owner, data=$info)\n"; 1507 | die; 1508 | } 1509 | if( length($value) > $layout->{ 'maxlength' } ) { 1510 | $layout->{ 'maxlength' } = length($value); 1511 | } 1512 | 1513 | return $value; 1514 | } 1515 | 1516 | sub printReportHeader { 1517 | if( $output_layout eq "list" ) { 1518 | # do nothing 1519 | } 1520 | else { 1521 | my $index = 0; 1522 | foreach my $info ( @layout ) { 1523 | my $value = $info->{'title'}; 1524 | printComponent( $value , $info , $index ); 1525 | $index = $index + 1; 1526 | } 1527 | print "\n"; 1528 | 1529 | 1530 | if( $output_layout eq "col" ) { 1531 | my $line = ""; 1532 | $index = 0; 1533 | foreach my $info ( @layout ) { 1534 | 1535 | my $field = ( '-' x $info->{'maxlength'} ); 1536 | if( $index > 0 ) { 1537 | $line = $line . "--" . $field; 1538 | } 1539 | else { 1540 | $line = $line . $field; 1541 | } 1542 | $index = $index + 1; 1543 | } 1544 | print $line . "\n"; 1545 | } 1546 | } 1547 | } 1548 | 1549 | sub printComponentHeader { 1550 | my ($first) = @_; 1551 | if( $output_layout eq "list" ) { 1552 | if( $first == 0 ) { 1553 | print "\n"; 1554 | print "---------------------\n"; 1555 | print "\n"; 1556 | } 1557 | } 1558 | else { 1559 | # do nothing 1560 | } 1561 | } 1562 | 1563 | sub printComponent { 1564 | my ($value,$layout,$index) = @_; 1565 | 1566 | my $fmt = "%s"; 1567 | my $sep = ";"; 1568 | my $prefix = ""; 1569 | my $suffix = ""; 1570 | 1571 | if( $output_layout eq "col" ) { 1572 | if( $layout->{'align'} eq "right" ) { 1573 | $fmt = "%+" . $layout->{'maxlength'} . "s"; 1574 | } 1575 | else { 1576 | $fmt = "%-" . $layout->{'maxlength'} . "s"; 1577 | } 1578 | $sep = " "; 1579 | } 1580 | if( $output_layout eq "list" ) { 1581 | $sep = ""; 1582 | $prefix = $layout->{'title'} . ": "; 1583 | $suffix = "\n"; 1584 | } 1585 | 1586 | if( $index > 0 ) { 1587 | print $sep; 1588 | } 1589 | print $prefix; 1590 | printf( $fmt , $value ); 1591 | print $suffix; 1592 | } 1593 | 1594 | sub printComponentFooter { 1595 | if( $output_layout eq "list" ) { 1596 | # do nothing 1597 | } 1598 | else { 1599 | print "\n"; 1600 | } 1601 | } 1602 | 1603 | sub printReportFooter { 1604 | # do nothing 1605 | } 1606 | 1607 | sub doReport { 1608 | my ($print) = @_; 1609 | 1610 | # loop each components 1611 | # 1612 | if ( $print > 0 ) { 1613 | printReportHeader; 1614 | } 1615 | 1616 | my %reportedDisks; 1617 | 1618 | my $first = 1; 1619 | if( $accept_label == 1 ) { 1620 | foreach my $label ( @partitionsKeys ) { 1621 | my $idev = $devices{ $partitions{ $label }->{ 'device' } }; 1622 | my $ilabel = $partitions{ $label }; 1623 | my $value = ""; 1624 | 1625 | # filters 1626 | if( applyFilterDevice($idev) == 0 ) { next; } 1627 | if( applyFilterLabel($ilabel) == 0 ) { next; } 1628 | 1629 | $reportedDisks{ $idev->{'device'} } = 1; 1630 | 1631 | my $index = 0; 1632 | if( $print > 0 ) { 1633 | printComponentHeader($first); 1634 | $first = 0; 1635 | 1636 | if( $locate > 0 ) { 1637 | if( exists( $sasdevices{ $idev->{'serial'} } ) ) { 1638 | if( !exists( $sasdevicesLocate{ $idev->{'serial'} } ) ) { 1639 | $sasdevicesLocate{ $idev->{'serial'} } = $sasdevices{ $idev->{'serial'} }; 1640 | } 1641 | } 1642 | } 1643 | } 1644 | foreach my $info ( @layout ) { 1645 | $value = getPrintableValue( $idev , $ilabel , $info ); 1646 | if( $print > 0 ) { 1647 | printComponent( $value , $info , $index ); 1648 | $index = $index + 1; 1649 | } 1650 | else { 1651 | if( $value ne "" ) { 1652 | $info->{'enabled'} = 1; 1653 | } 1654 | } 1655 | } 1656 | if( $print > 0 ) { 1657 | printComponentFooter; 1658 | } 1659 | } 1660 | } 1661 | foreach my $dev ( @devicesKeys ) { 1662 | my $idev = $devices{ $dev }; 1663 | my $ilabel = ""; 1664 | 1665 | if( ( $idev->{ 'partitioned' } == 1 ) && ( $accept_label == 1 ) ) { 1666 | next; 1667 | } 1668 | 1669 | # filters 1670 | if( applyFilterDevice($idev) == 0 ) { next; } 1671 | if( applyFilterLabel($ilabel) == 0 ) { next; } 1672 | 1673 | $reportedDisks{ $idev->{'device'} } = 1; 1674 | 1675 | my $index = 0; 1676 | 1677 | if( $print > 0 ) { 1678 | printComponentHeader($first); 1679 | $first = 0; 1680 | 1681 | if( $locate > 0 ) { 1682 | if( exists( $sasdevices{ $idev->{'serial'} } ) ) { 1683 | if( !exists( $sasdevicesLocate{ $idev->{'serial'} } ) ) { 1684 | $sasdevicesLocate{ $idev->{'serial'} } = $sasdevices{ $idev->{'serial'} }; 1685 | } 1686 | } 1687 | } 1688 | } 1689 | foreach my $info ( @layout ) { 1690 | my $value = getPrintableValue( $idev , $ilabel , $info ); 1691 | if( $print > 0 ) { 1692 | printComponent( $value , $info , $index ); 1693 | $index = $index + 1; 1694 | } 1695 | else { 1696 | if( $value ne "" ) { 1697 | $info->{'enabled'} = 1; 1698 | } 1699 | } 1700 | } 1701 | if( $print > 0 ) { 1702 | printComponentFooter; 1703 | } 1704 | } 1705 | if( $print > 0 ) { 1706 | printReportFooter; 1707 | } 1708 | 1709 | if ( $print > 0 ) { 1710 | print "\n"; 1711 | print "" . ( scalar keys %reportedDisks ) . " selected disk(s)\n"; 1712 | } 1713 | } 1714 | 1715 | sub doLocate() { 1716 | if( $locate == 0 ) { 1717 | return; 1718 | } 1719 | if( scalar keys %sasdevicesLocate == 0 ) { 1720 | return; 1721 | } 1722 | 1723 | my $blink_mode = 1; 1724 | my $saslocate = "ON"; 1725 | my $sas; 1726 | 1727 | print "\n"; 1728 | print "Locating devices "; 1729 | 1730 | # store STDOUT 1731 | open (my $STDOLD, '>&', STDOUT); 1732 | 1733 | for( my $count = 0 ; $count < $locate ; $count++ ) { 1734 | if( $blink_mode > 0 ) { 1735 | $saslocate = "ON"; 1736 | } 1737 | else { 1738 | $saslocate = "OFF"; 1739 | } 1740 | 1741 | open( STDOUT , ">" , "/dev/null" ); 1742 | 1743 | foreach my $serial ( keys %sasdevicesLocate ) { 1744 | $sas = $sasdevicesLocate{ $serial }; 1745 | 1746 | if( $sas->{'sas'} eq "sas2" ) { 1747 | system( "sas2ircu" , $sas->{'controller'} , "locate" , $sas->{'enclosure'} . ":" . $sas->{'slot'} , $saslocate ); 1748 | } 1749 | elsif( $sas->{'sas'} eq "sas3" ) { 1750 | system( "sas3ircu" , $sas->{'controller'} , "locate" , $sas->{'enclosure'} . ":" . $sas->{'slot'} , $saslocate ); 1751 | } 1752 | } 1753 | 1754 | # restore STDOUT 1755 | open (STDOUT, '>&', $STDOLD); 1756 | 1757 | print "."; 1758 | $blink_mode = $blink_mode * -1; 1759 | sleep(1); 1760 | } 1761 | 1762 | print "\n"; 1763 | } 1764 | 1765 | sub scan { 1766 | # get host configuration 1767 | # 1768 | parseDisks(); 1769 | parsePartitions(); 1770 | parseLabels(); 1771 | parseGeli(); 1772 | parseMultiPath(); 1773 | parseZpools(); 1774 | parseSasDevices("sas2ircu"); 1775 | parseSasDevices("sas3ircu"); 1776 | completeDiskType(); 1777 | #printPartitions(); 1778 | 1779 | # sort devices/partitions before filtering them 1780 | # 1781 | @partitionsKeys = sort( { compare_labels($a,$b) } keys %partitions ); 1782 | @devicesKeys = sort( { compare_devices($a,$b) } keys %devices ); 1783 | } 1784 | 1785 | sub report { 1786 | my ($display_columns) = @_; 1787 | 1788 | # compute the layout (except padding) 1789 | # 1790 | prepareOutputLayout($display_columns); 1791 | 1792 | doReport(0); # just compute padding 1793 | 1794 | # check unused columns 1795 | my $shrinked = 0; 1796 | if( $accept_collapse == 1 ) { 1797 | foreach my $col ( @layout ) { 1798 | if( $col->{'enabled'} == 0 ) { 1799 | $_ = $display_columns; 1800 | eval sprintf "tr/%s//d", map quotemeta, $col->{'col'}; 1801 | $display_columns = $_; 1802 | $shrinked = 1; 1803 | } 1804 | } 1805 | } 1806 | 1807 | if( $output_sort eq "" ) { 1808 | if( $display_columns =~ m/z/ ) { 1809 | $output_sort = $output_sort . "z"; 1810 | } 1811 | if( $display_columns =~ m/Z/ ) { 1812 | $output_sort = $output_sort . "Z"; 1813 | } 1814 | if( $display_columns =~ m/m/ ) { 1815 | $output_sort = $output_sort . "m"; 1816 | } 1817 | if( $display_columns =~ m/o/ ) { 1818 | $output_sort = $output_sort . "o"; 1819 | } 1820 | if( $display_columns =~ m/d/ ) { 1821 | $output_sort = $output_sort . "d"; 1822 | } 1823 | if( $display_columns =~ m/p/ ) { 1824 | $output_sort = $output_sort . "p"; 1825 | } 1826 | 1827 | @partitionsKeys = sort( { compare_labels($a,$b) } keys %partitions ); 1828 | @devicesKeys = sort( { compare_devices($a,$b) } keys %devices ); 1829 | } 1830 | 1831 | if( $shrinked == 1 ) { 1832 | prepareOutputLayout($display_columns); 1833 | doReport(0); 1834 | } 1835 | 1836 | doReport(1); # real reporting 1837 | doLocate(); 1838 | } 1839 | 1840 | if( caller ) { 1841 | parseArgumentsDiskList(); 1842 | } 1843 | else { 1844 | parseArgumentsDiskList(@ARGV); 1845 | scan(); 1846 | report( $output_columns ); 1847 | } 1848 | 1849 | 1; 1850 | --------------------------------------------------------------------------------