From d65b40e60ad7e7014dbd4c74236160eb8de4d1b4 Mon Sep 17 00:00:00 2001 From: Harald Hope Date: Sat, 14 Mar 2020 23:15:16 -0700 Subject: [PATCH] Bugs: 1. Fixed undefined error that could happen, in rare cases, in hdd_temp logic. Fixes: 1. Fixed Elbrus cpu nazming, model 9 is 8CV, not 8CB (Cyrillic error) 2. Preventitive, was not using '-' quite correctly in all regex ranges. 3. Had wrong desktop string listed in Unity 4. Reordered Family/Drive model in usb drive reports, it's to make it more obvious what is what. 5. Adjusted indexing of splits to get better results in corner cases. 6. Fixed some numbering issues. 7. Added trimming n1 from nvme0 type names for nvme, this corrects some issues users were having. 8. Fixed a division by 0 error in smartctl data grabber. 9. Fixed a Perl issue, didn't realize perl treats 000 as a string, not 0. 10. Another Perl fix, int() only wants to get numeric values sent to it, I'd assumed a different behavior, non numerics get converted to 0, but that's not how Perl sees things. Things like this, by the way, are why Perl is so absurdly fast. Enhancements: 1. More disk vendors. The list will never be complete!! We have found eternal churn!! Thanks to linux lite hardware database as always. 2. Big one!!! Now inxi uses smartctl data, if installed, for getting advanced drive information (with -a). See man and help for details. Will show failing drives, etc. Lots of info can be available, but sometimes data is not in smartctl db, so inxi can't find it, that's not an inxi bug, it's just how it is. 3. Made hours on more human readable, into days/hours, for -a smartctl disk report. 4. Added $test[12] for smartctl data printout, and $test[13] for disk array print out. Note that advanced debugger outputs can change or vary depending on what is being worked on so don't in general rely on these always being around. But they do tend to say stuck in place once I add them. 5. Added some nvme stuff, spare reserve, if you need it, you'll appreciate it, if not, you'll never know it's there. 6. By request from some forum issue thread: made --host only be shown onif not --filter or not --host. This makes -z remove hostname, but retains ability to do absolute overrides. Hostname should have always been filtered out like that, it was an oversight. I think that was Manjaro who asked that, but I forget. Note that this change, as usual, will not alter expected behaviors if users have config item for hostname set. 7. Added support for picom compositor, thanks user codebling for that, I think that's compiz fork, the real branch that is that is being developed. --- inxi | 727 +++++++++++++++++++++++++++++++++++++++++++------ inxi.1 | 50 +++- inxi.changelog | 55 ++++ 3 files changed, 736 insertions(+), 96 deletions(-) diff --git a/inxi b/inxi index e65a29f..039f179 100755 --- a/inxi +++ b/inxi @@ -31,8 +31,8 @@ use POSIX qw(uname strftime ttyname); ## INXI INFO ## my $self_name='inxi'; -my $self_version='3.0.37'; -my $self_date='2019-11-19'; +my $self_version='3.0.38'; +my $self_date='2020-03-14'; my $self_patch='00'; ## END INXI INFO ## @@ -52,7 +52,7 @@ if (eval {require Time::HiRes}){ } @t0 = eval 'Time::HiRes::gettimeofday()' if $b_hires; # let's start it right away ## Hashes -my ( %alerts,%client,%colors,%debugger,%dl,%files,%rows,%system_files,%use ); +my (%alerts,%client,%colors,%debugger,%dl,%files,%rows,%system_files); ## Arrays # ps_aux is full output, ps_cmd is only the last 10 columns to last @@ -77,9 +77,13 @@ $b_slot_tool,$b_soc_audio,$b_soc_gfx,$b_soc_net,$b_soc_timer,$b_sparc, $b_sudo,$b_sysctl,$b_usb,$b_usb_check,$b_usb_sys,$b_usb_tool,$b_wmctrl); ## Disk checks my ($b_dm_boot_disk,$b_dm_boot_optical,$b_glabel,$b_hardware_raid, -$b_label_uuid,$b_lsblk,$b_partitions,$b_raid); -my ($b_sysctl_disk,$b_update,$b_weather) = (1,1,1); - +$b_label_uuid,$b_lsblk,$b_partitions,$b_raid,$b_smartctl); +# initialize basic use features +my %use = ( +'sysctl_disk' => 1, # unused currently +'update' => 1, # switched off/on with maintainer config ALLOW_UPDATE +'weather' => 1, # switched off/on with maintainer config ALLOW_WEATHER +); ## System my ($bsd_type,$device_vm,$language,$os,$pci_tool,$wan_url) = ('','','','','',''); my ($bits_sys,$cpu_arch); @@ -109,9 +113,8 @@ my %sep = ( 's2-irc' => '', 's2-console' => ':', ); - -my %show = ('host' => 1); - +my %show; +#$show{'host'} = 1; my %size = ( 'console' => 115, # Default indentation level. NOTE: actual indent is 1 greater to allow for @@ -287,7 +290,7 @@ sub check_tools { ); %commands = (%commands,%hash); } - # can't check permissions since we need to know the partition + # can't check permissions since we need to know the partition/disc if ($b_block_tool){ %hash = ( 'blockdev' => 'linux', @@ -295,6 +298,12 @@ sub check_tools { ); %commands = (%commands,%hash); } + if ($b_smartctl){ + %hash = ( + 'smartctl' => 'all', + ); + %commands = (%commands,%hash); + } foreach ( keys %commands ){ $action = 'use'; $message = 'Present and working'; @@ -1065,8 +1074,8 @@ sub get_configs { # args: 0: key; 1: value sub get_config_item { my ($key,$val) = @_; - if ($key eq 'ALLOW_UPDATE' || $key eq 'B_ALLOW_UPDATE') {$b_update = $val if is_int($val)} - elsif ($key eq 'ALLOW_WEATHER' || $key eq 'B_ALLOW_WEATHER') {$b_weather = $val if is_int($val)} + if ($key eq 'ALLOW_UPDATE' || $key eq 'B_ALLOW_UPDATE') {$use{'update'} = $val if is_int($val)} + elsif ($key eq 'ALLOW_WEATHER' || $key eq 'B_ALLOW_WEATHER') {$use{'weather'} = $val if is_int($val)} elsif ($key eq 'CPU_SLEEP') {$cpu_sleep = $val if is_numeric($val)} elsif ($key eq 'DL_TIMEOUT') {$dl_timeout = $val if is_int($val)} elsif ($key eq 'DOWNLOADER') { @@ -1084,7 +1093,12 @@ sub get_config_item { elsif ($key eq 'PARTITION_SORT') {$show{'partition-sort'} = $val if ($val =~ /^(dev-base|fs|id|label|percent-used|size|uuid|used)$/) } elsif ($key eq 'PS_COUNT') {$ps_count = $val if is_int($val) } elsif ($key eq 'SENSORS_CPU_NO') {$sensors_cpu_nu = $val if is_int($val)} - elsif ($key eq 'SHOW_HOST' || $key eq 'B_SHOW_HOST') { $show{'host'} = $val if is_int($val)} + elsif ($key eq 'SHOW_HOST' || $key eq 'B_SHOW_HOST') { + if (is_int($val)){ + $show{'host'} = $val; + $show{'no-host'} = 1 if !$show{'host'}; + } + } elsif ($key eq 'USB_SYS') {$b_usb_sys = $val if is_int($val)} elsif ($key eq 'WAN_IP_URL') { if ($val =~ /^(ht|f)tp[s]?:\//i){ @@ -1435,7 +1449,6 @@ sub audio_data { else { $data{'proc-asound-codecs'} = undef; } - write_data(\%data,'audio'); @files = ( '/proc/asound/cards', @@ -1721,6 +1734,8 @@ sub system_data { # fdisk @cmds = ( ['clang','--version'], + # only for prospective ram feature data collection: requires i2c-tools and module eeprom loaded + ['decode-dimms',''], ['dmidecode',''], ['dmesg',''], ['gcc','--version'], @@ -1820,7 +1835,7 @@ sub run_self { my $i = ($option eq 'main-full')? ' -i' : ''; my $z = ($debugger{'z'}) ? ' -z' : ''; my $iz = "$i$z"; - $iz =~ s/[\s\-]//g; + $iz =~ s/[\s-]//g; my $cmd = "$self_path/$self_name -FRfrploudmaxxx$i$z --usb --slots --debug 10 -y 120 > $data_dir/$self_name-FRfrploudmaxxx$iz-usb-slots-y120.txt 2>&1"; system($cmd); copy($log_file, "$data_dir") or main::error_handler('copy-failed', "$log_file", "$!"); @@ -2869,11 +2884,11 @@ sub item_data { 'rpm' => 'lm-sensors', }), 'smartctl' => ({ - 'info' => '-Dxx rotation (alt), type, smart', - 'info-bsd' => '-Dx rotation (alt), type, smart', - 'apt' => '', - 'pacman' => '', - 'rpm' => '', + 'info' => '-Da advanced data', + 'info-bsd' => '-Da advanced data', + 'apt' => 'smartmontools', + 'pacman' => 'smartmontools', + 'rpm' => 'smartmontools', }), 'strings' => ({ 'info' => '-I sysvinit version', @@ -3124,8 +3139,8 @@ sub compare_versions { elsif ($two && !$one){return $two;} elsif (!$one && !$two){return} my ($pad1,$pad2) = ('',''); - my (@temp1) = split /[\.\-_]/, $one; - my (@temp2) = split /[\.\-_]/, $two; + my (@temp1) = split /[._-]/, $one; + my (@temp2) = split /[._-]/, $two; @temp1 = map {$_ = sprintf("%04s", $_);$_ } @temp1; @temp2 = map {$_ = sprintf("%04s", $_);$_ } @temp2; $pad1 = join '', @temp1; @@ -3354,6 +3369,7 @@ sub program_values { 'papyros' => ['^papyros',0,'0','papyros',0,1,0], 'pekwm' => ['^pekwm',3,'--version','PekWM',0,1,0], 'perceptia' => ['^perceptia',0,'0','perceptia',0,1,0], + 'picom' => ['^\d',1,'--version','Picom',0,1,0], 'plasmashell' => ['^plasmashell',2,'--version','KDE Plasma',0,1,0], 'qtdiag' => ['^qt',2,'--version','Qt',0,1,0], 'qtile' => ['^qtile',0,'0','Qtile',0,0,1], @@ -3818,8 +3834,8 @@ sub get_options{ eval $start if $b_log; my (@args) = @_; $show{'short'} = 1; - my ($b_downloader,$b_help,$b_no_man,$b_no_man_force,$b_recommends,$b_updater,$b_version, - $b_use_man,$self_download, $download_id); + my ($b_downloader,$b_help,$b_no_man,$b_no_man_force,$b_recommends,$b_updater, + $b_version,$b_use_man,$self_download, $download_id); GetOptions ( 'a|admin' => sub { $b_admin = 1;}, @@ -4067,7 +4083,7 @@ sub get_options{ my ($opt) = @_; $show{'short'} = 0; $b_downloader = 1; - if ( $b_weather ){ + if ( $use{'weather'} ){ $show{'weather'} = 1; } else { @@ -4079,7 +4095,7 @@ sub get_options{ $arg =~ s/\s//g; $show{'short'} = 0; $b_downloader = 1; - if ( $b_weather ){ + if ( $use{'weather'} ){ if ($arg){ $show{'weather'} = 1; $show{'weather-location'} = $arg; @@ -4132,9 +4148,9 @@ sub get_options{ error_handler('bad-arg', $opt, $arg); } }, 'z|filter' => sub { - $show{'filter'} = 1; }, + $use{'filter'} = 1; }, 'Z|filter-override' => sub { - $show{'filter-override'} = 1; }, + $use{'filter-override'} = 1; }, ## Start non data options 'alt:i' => sub { my ($opt,$arg) = @_; @@ -4269,7 +4285,8 @@ sub get_options{ 'h|help|?' => sub { $b_help = 1; }, 'host|hostname' => sub { - $show{'host'} = 1 }, + $show{'host'} = 1; + $show{'no-host'} = 0}, 'indent-min:i' => sub { my ($opt,$arg) = @_; if ($arg =~ /^\d+$/){ @@ -4298,7 +4315,8 @@ sub get_options{ error_handler('bad-arg', $opt, $arg); }}, 'no-host|no-hostname' => sub { - $show{'host'} = 0 }, + $show{'host'} = 0 ; + $show{'no-host'} = 1}, 'no-man' => sub { $b_no_man_force = 0; }, 'no-ssl' => sub { @@ -4331,7 +4349,7 @@ sub get_options{ 'U|update:s' => sub { # 1,2,3 OR http://myserver/path/inxi my ($opt,$arg) = @_; $b_downloader = 1; - if ( $b_update ){ + if ( $use{'update'} ){ $b_updater = 1; if (!$arg && $self_name eq 'pinxi'){ $b_man = 1; @@ -4402,10 +4420,14 @@ sub get_options{ } $b_block_tool = 1 if ( $b_admin && ($show{'partition'} || $show{'partition-full'} )); $b_sudo = 1 if ( $show{'unmounted'} || ($extra > 0 && $show{'disk'}) ); + $extra = 3 if $b_admin; + $use{'filter'} = 0 if $use{'filter-override'}; # override for things like -b or -v2 to -v3 $show{'cpu-basic'} = 0 if $show{'cpu'}; $show{'optical-basic'} = 0 if $show{'optical'}; $show{'partition'} = 0 if $show{'partition-full'}; + $show{'host'} = 0 if $show{'no-host'}; + $show{'host'} = 1 if ($show{'host'} || (!$use{'filter'} && !$show{'no-host'})); if ($show{'disk'} || $show{'optical'} ){ $show{'disk-basic'} = 0; $show{'disk-total'} = 0; @@ -4424,15 +4446,16 @@ sub get_options{ $show{'info'} || $show{'machine'} || $show{'process'} || $show{'ram'} || $show{'sensor'} ) ){ $b_sysctl = 1; } - if ($show{'filter-override'}){ - $show{'filter'} = 0; - } if ($bsd_type && ($show{'short'} || $show{'disk-basic'} || $show{'disk-total'} || $show{'disk'})){ $b_dm_boot_disk = 1; } if ($bsd_type && ($show{'optical-basic'} || $show{'optical'})){ $b_dm_boot_optical = 1 } + if ($b_admin && $show{'disk'}){ + $b_smartctl = 1; + } + } sub show_options { @@ -4466,10 +4489,11 @@ sub show_options { ['0', '', '', $line ], ['0', '', '', "Output Control Options:" ], ['1', '-a', '--admin', "Adds advanced sys admin data (only works with - verbose or line output, not short form):" ], + verbose or line output, not short form), sets --extra=3:" ], ['2', '-C', '', "If available: CPU vulnerabilities (bugs); family, model-id, stepping - format: hex (decimal) if greater than 9, otherwise hex; microcode - format: hex." ], - ['2', '-d,-D', '', "If available: logical and physical block sizes." ], + ['2', '-d,-D', '', "If available: logical and physical block sizes; drive family; + USB drive specifics; SMART report." ], ['2', '-p,-P', '', "If available: raw size of partition, percent available for user, block size of file system (root required); for swap, shows swapiness and vfs cache pressure, and if values are default or not." ], @@ -4574,7 +4598,7 @@ sub show_options { ); push @data, @rows; # if distro maintainers don't want the weather feature disable it - if ( $b_weather ){ + if ( $use{'weather'} ){ @rows = ( ['1', '-w', '--weather', "Local weather data/time. To check an alternate location, see -W. NO AUTOMATED QUERIES ALLOWED!"], @@ -4628,7 +4652,7 @@ sub show_options { ['2', '--usb', '', "For Device: driver." ], ); push @data, @rows; - if ( $b_weather ){ + if ( $use{'weather'} ){ @rows = (['2', '-w -W', '', "Wind speed and direction, humidity, pressure, and time zone, if available." ]); push @data, @rows; @@ -4660,7 +4684,7 @@ sub show_options { ['2', '--usb', '', "Vendor:chip ID." ], ); push @data, @rows; - if ( $b_weather ){ + if ( $use{'weather'} ){ @rows = (['2', '-w -W', '', "Snow, rain, precipitation, (last observed hour), cloud cover, wind chill, dew point, heat index, if available." ]); push @data, @rows; @@ -4689,7 +4713,7 @@ sub show_options { ['2', '--usb', '', "For Device: serial number (if present), interface count; USB speed." ], ); push @data, @rows; - if ( $b_weather ){ + if ( $use{'weather'} ){ @rows = (['2', '-w -W', '', "Location (uses -z/irc filter), weather observation time, altitude, sunrise/sunset, if available." ] ); push @data, @rows; @@ -4698,8 +4722,8 @@ sub show_options { ['1', '-y', '--width', "Output line width max (integer >= 80). Overrides IRC/Terminal settings or actual widths. Example:^inxi^-y^130" ], ['1', '-z', '--filter', "Adds security filters for IP/MAC addresses, serial numbers, - location (-w), user home directory name. Default on for IRC clients." ], - ['1', '-Z', '--filter-override', "Absolute override for output filters. Useful for + location (-w), user home directory name, host item. Default on for IRC clients." ], + ['1', '-Z', '--filter-override', "Override for output filters. Useful for debugging networking issues in IRC, for example." ], [0, '', '', "$line" ], [0, '', '', "Additional Options:" ], @@ -4709,7 +4733,7 @@ sub show_options { for that feature." ] ); push @data, @rows; - if ( $b_update ){ + if ( $use{'update'} ){ @rows = ( ['1', '-U', '--update', "Auto-update $self_name. Will also install/update man page. Note: if you installed as root, you must be root to update, otherwise @@ -4745,7 +4769,7 @@ sub show_options { (default 10; -1 removes limit)." ], ); push @data, @rows; - if ( $b_update ){ + if ( $use{'update'} ){ @rows = ( ['1', '', '--man', "Install correct man version for dev branch (-U 3) or pinxi using -U." ], ); @@ -4755,7 +4779,7 @@ sub show_options { ['1', '', '--no-host', "Turn off hostname for -S. Useful if showing output from servers etc." ], ); push @data, @rows; - if ( $b_update ){ + if ( $use{'update'} ){ @rows = ( ['1', '', '--no-man', "Disable man install for all -U update actions." ], ); @@ -4913,7 +4937,7 @@ sub get_client_data { main::get_shell_data($ppid); } else { - $show{'filter'} = 1; + $use{'filter'} = 1; get_client_name(); if ($client{'konvi'} == 1 || $client{'konvi'} == 3){ set_konvi_data(); @@ -5258,7 +5282,7 @@ sub set_konvi_data { sub apply_filter { my ($string) = @_; if ($string){ - $string = ( $show{'filter'} ) ? $filter_string : $string; + $string = ( $use{'filter'} ) ? $filter_string : $string; } else { $string = 'N/A'; @@ -5475,10 +5499,15 @@ sub row_defaults { 'ps-data-null' => "No Process data available.", 'raid-data' => "No RAID data was found.", 'ram-data' => "No RAM data was found.", - 'root-required' => "", + 'root-required' => "", 'sensors-data-ipmi' => "No ipmi sensors data was found.", 'sensors-data-linux' => "No sensors data was found. Is sensors configured?", 'sensors-ipmi-root' => "Unable to run ipmi sensors. Root privileges required.", + 'smartctl-command-failed' => "A mandatory SMART command failed. Various possible causes.", + 'smartctl-root' => "Unable to run smartctl. Root privileges required.", + 'smartctl-udma-crc' => "Bad cable/connection?", + 'smartctl-usb' => "Unknown USB bridge. Flash drive/Unsupported enclosure?", + 'smartctl-unknown' => "Unknown smartctl error. Unable to get data.", 'tool-missing' => "", 'unmounted-data' => "No unmounted partitions found.", 'unmounted-data-bsd' => "No unmounted partition data found for this BSD system.", @@ -7188,18 +7217,18 @@ sub data_sysctl { # freebsd 10: hw.model: AMD Athlon(tm) II X2 245 Processor $line[1] = main::cleaner($line[1]); $line[1] = cpu_cleaner($line[1]); - if ( $line[1] =~ /([0-9]+)[\-[:space:]]*([KM]B)\s+L2 cache/) { + if ( $line[1] =~ /([0-9]+)[-[:space:]]*([KM]B)\s+L2 cache/) { my $multiplier = ($2 eq 'MB') ? 1024: 1; $cpu{'l2-cache'} = $1 * $multiplier; } - if ( $line[1] =~ /([^0-9\.][0-9\.]+)[\-[:space:]]*[MG]Hz/) { + if ( $line[1] =~ /([^0-9\.][0-9\.]+)[-[:space:]]*[MG]Hz/) { $cpu{'max-freq'} = $1; if ($cpu{'max-freq'} =~ /MHz/i) { - $cpu{'max-freq'} =~ s/[\-[:space:]]*MHz//; + $cpu{'max-freq'} =~ s/[-[:space:]]*MHz//; $cpu{'max-freq'} = speed_cleaner($cpu{'max-freq'},'mhz'); } elsif ($cpu{'max-freq'} =~ /GHz/) { - $cpu{'max-freq'} =~ s/[\-[:space:]]*GHz//i; + $cpu{'max-freq'} =~ s/[-[:space:]]*GHz//i; $cpu{'max-freq'} = $cpu{'max-freq'} / 1000; $cpu{'max-freq'} = speed_cleaner($cpu{'max-freq'},'mhz'); } @@ -8015,7 +8044,7 @@ sub cpu_arch { else {$arch = 'Elbrus-8C';} } # note: stepping > 1 may be 8C1 elsif ( $model eq '8' ) {$arch = 'Elbrus-1C+'} - elsif ( $model eq '9' ) {$arch = 'Elbrus-8CB'} + elsif ( $model eq '9' ) {$arch = 'Elbrus-8CV'} elsif ( $model eq '10' ) {$arch = 'Elbrus-12C'} elsif ( $model eq '11' ) {$arch = 'Elbrus-16C'} elsif ( $model eq '12' ) {$arch = 'Elbrus-2C3'} @@ -8172,10 +8201,11 @@ sub hex_and_decimal { ## DiskData { package DiskData; -my ($b_hddtemp,$b_nvme); +my ($b_hddtemp,$b_nvme,$smartctl_missing); my ($hddtemp,$nvme) = ('',''); my (@by_id,@by_path); - +my ($debugger_dir); +# main::writer("$debugger_dir/system-repo-data-urpmq.txt",@data2) if $debugger_dir; sub get { eval $start if $b_log; my (@data,@rows,$key1,$val1); @@ -8234,9 +8264,84 @@ sub create_output { eval $start if $b_log; my (@disks) = @_; #print Data::Dumper::Dumper \@disks; - my (@data,@rows); + my ($b_oldage,$b_prefail,$b_smart_permissions,@data,@rows); my ($num,$j) = (0,0); - my ($id,$model,$size,$used,$percent,$size_holder,$used_holder) = ('','','','','','',''); + my ($id,$model,$size,$used,$percent,$size_holder, + $used_holder) = ('','','','','','','','',''); + my @smart_basic =( + ['smart','SMART'], + ['smart-error','SMART Message'], + ['smart-support','state'], + ['smart-status','health'], + ['smart-power-on-hours','on'], + ['smart-cycles','cycles'], + ['smart-units-read','read-units'], + ['smart-units-written','written-units'], + ['smart-read','read'], + ['smart-written','written'], + ); + my @smart_age =( + ['smart-gsense-error-rate-r','g-sense error rate'], + ['smart-media-wearout-v','media wearout'], + ['smart-media-wearout-t','threshold'], + ['smart-media-wearout-f','alert'], + ['smart-multizone-errors-v','write error rate'], + ['smart-multizone-errors-t','threshold'], + ['smart-udma-crc-errors-r','UDMA CRC errors'], + ['smart-udma-crc-errors-f','alert'], + ); + my @smart_fail =( + ['smart-end-to-end-v','end-to-end'], + ['smart-end-to-end-t','threshold'], + ['smart-end-to-end-f','alert'], + ['smart-raw-read-error-rate-v','read error rate'], + ['smart-raw-read-error-rate-t','threshold'], + ['smart-raw-read-error-rate-f','alert'], + ['smart-reallocated-sectors-v','reallocated sector'], + ['smart-reallocated-sectors-t','threshold'], + ['smart-reallocated-sectors-f','alert'], + ['smart-retired-blocks-v','retired block'], + ['smart-retired-blocks-t','threshold'], + ['smart-retired-blocks-f','alert'], + ['smart-runtime-bad-block-v','runtime bad block'], + ['smart-runtime-bad-block-t','threshold'], + ['smart-runtime-bad-block-f','alert'], + ['smart-seek-error-rate-v', 'seek error rate'], + ['smart-seek-error-rate-t', 'threshold'], + ['smart-seek-error-rate-f', 'alert'], + ['smart-spinup-time-v','spin-up time'], + ['smart-spinup-time-t','threshold'], + ['smart-spinup-time-f','alert'], + ['smart-ssd-life-left-v','life left'], + ['smart-ssd-life-left-t','threshold'], + ['smart-ssd-life-left-f','alert'], + ['smart-unused-reserve-block-v','unused reserve block'], + ['smart-unused-reserve-block-t','threshold'], + ['smart-unused-reserve-blockf','alert'], + ['smart-used-reserve-block-v','used reserve block'], + ['smart-used-reserve-block-t','threshold'], + ['smart-used-reserve-block-f','alert'], + ['smart-unknown-1-a','attribute'], + ['smart-unknown-1-v','value'], + ['smart-unknown-1-w','worst'], + ['smart-unknown-1-t','threshold'], + ['smart-unknown-1-f','alert'], + ['smart-unknown-2-a','attribute'], + ['smart-unknown-2-v','value'], + ['smart-unknown-2-w','worst'], + ['smart-unknown-2-t','threshold'], + ['smart-unknown-2-f','alert'], + ['smart-unknown-3-a','attribute'], + ['smart-unknown-3-v','value'], + ['smart-unknown-3-w','worst'], + ['smart-unknown-3-t','threshold'], + ['smart-unknown-4-f','alert'], + ['smart-unknown-4-a','attribute'], + ['smart-unknown-4-v','value'], + ['smart-unknown-4-w','worst'], + ['smart-unknown-4-t','threshold'], + ['smart-unknown-4-f','alert'], + ); my @sizing = main::get_size($disks[0]{'size'}) if $disks[0]{'size'}; #print Data::Dumper::Dumper \@disks; if (@sizing){ @@ -8267,10 +8372,14 @@ sub create_output { }); @rows = (@rows,@data); shift @disks; + if ($smartctl_missing){ + $j = scalar @rows; + $rows[$j]{main::key($num++,'SMART Message')} = $smartctl_missing; + } if ( $show{'disk'} && @disks){ @disks = sort { $a->{'id'} cmp $b->{'id'} } @disks; foreach my $ref (@disks){ - ($id,$model,$size) = ('','',''); + ($b_oldage,$b_prefail,$id,$model,$size) = (0,0,'','',''); my %row = %$ref; $num = 1; $model = ($row{'model'}) ? $row{'model'}: 'N/A'; @@ -8290,17 +8399,31 @@ sub create_output { $size = 'N/A'; } $j = scalar @rows; + if (!$b_smart_permissions && $row{'smart-permissions'}){ + $b_smart_permissions = 1; + $rows[$j]{main::key($num++,'SMART Message')} = $row{'smart-permissions'}; + $j = scalar @rows; + } @data = ({ main::key($num++,'ID') => $id, }); @rows = (@rows,@data); if ($row{'type'}){ - $rows[$j]{main::key($num++,'type')} = $row{'type'}, + $rows[$j]{main::key($num++,'type')} = $row{'type'}; } if ($row{'vendor'}){ - $rows[$j]{main::key($num++,'vendor')} = $row{'vendor'}, + $rows[$j]{main::key($num++,'vendor')} = $row{'vendor'}; } $rows[$j]{main::key($num++,'model')} = $model; + if ($row{'drive-vendor'}){ + $rows[$j]{main::key($num++,'drive vendor')} = $row{'drive-vendor'}; + } + if ($row{'drive-model'}){ + $rows[$j]{main::key($num++,'drive model')} = $row{'drive-model'}; + } + if ($row{'family'}){ + $rows[$j]{main::key($num++,'family')} = $row{'family'}; + } $rows[$j]{main::key($num++,'size')} = $size; if ($b_admin && $row{'block-physical'}){ $rows[$j]{main::key($num++,'block size')} = ''; @@ -8308,6 +8431,9 @@ sub create_output { $rows[$j]{main::key($num++,'logical')} = ($row{'block-logical'}) ? $row{'block-logical'} . ' B' : 'N/A'; } if ($extra > 1 && $row{'speed'}){ + if ($row{'sata'}){ + $rows[$j]{main::key($num++,'sata')} = $row{'sata'}; + } $rows[$j]{main::key($num++,'speed')} = $row{'speed'}; $rows[$j]{main::key($num++,'lanes')} = $row{'lanes'} if $row{'lanes'}; } @@ -8317,9 +8443,15 @@ sub create_output { if ($extra > 1){ my $serial = main::apply_filter($row{'serial'}); $rows[$j]{main::key($num++,'serial')} = $serial; + if ($row{'drive-serial'}){ + $rows[$j]{main::key($num++,'drive serial')} = main::apply_filter($row{'drive-serial'}); + } if ($row{'firmware'}){ $rows[$j]{main::key($num++,'rev')} = $row{'firmware'}; } + if ($row{'drive-firmware'}){ + $rows[$j]{main::key($num++,'drive rev')} = $row{'drive-firmware'}; + } } if ($extra > 0 && $row{'temp'}){ $rows[$j]{main::key($num++,'temp')} = $row{'temp'} . ' C'; @@ -8328,9 +8460,37 @@ sub create_output { if (defined $row{'partition-table'}){ $rows[$j]{main::key($num++,'scheme')} = $row{'partition-table'}; } + if ($row{'smart'} || $row{'smart-error'}){ + $j = scalar @rows; + ## Basic SMART and drive info ## + for (my $i = 0; $i < scalar @smart_basic;$i++){ + if ($row{$smart_basic[$i][0]}){ + $rows[$j]{main::key($num++,$smart_basic[$i][1])} = $row{$smart_basic[$i][0]}; + } + } + ## Old-Age errors ## + for (my $i = 0; $i < scalar @smart_age;$i++){ + if ($row{$smart_age[$i][0]}){ + if (!$b_oldage){ + $rows[$j]{main::key($num++,'Old-Age')} = ''; + $b_oldage = 1; + } + $rows[$j]{main::key($num++,$smart_age[$i][1])} = $row{$smart_age[$i][0]}; + } + } + ## Pre-Fail errors ## + for (my $i = 0; $i < scalar @smart_fail;$i++){ + if ($row{$smart_fail[$i][0]}){ + if (!$b_prefail){ + $rows[$j]{main::key($num++,'Pre-Fail')} = ''; + $b_prefail = 1; + } + $rows[$j]{main::key($num++,$smart_fail[$i][1])} = $row{$smart_fail[$i][0]}; + } + } + } } } - eval $end if $b_log; return @rows; } @@ -8363,7 +8523,16 @@ sub disk_data { else { @data = dmesg_boot_data($used); } - #print Data::Dumper::Dumper \@data; + if ($b_admin){ + my $ref = $alerts{'smartctl'}; + if ( $ref && $$ref{'action'} eq 'use'){ + @data = smartctl_data(@data); + } + else { + $smartctl_missing = $$ref{'missing'}; + } + } + print Data::Dumper::Dumper \@data if $test[13];; main::log_data('data',"used: $used") if $b_log; eval $end if $b_log; return @data; @@ -8499,10 +8668,10 @@ sub proc_data_advanced { } main::log_data('data',"working path: $working_path") if $b_log; if ($b_admin && -e "/sys/block/"){ - my @working = admin_data($drives[$i]{'id'}); - $drives[$i]{'block-logical'} = $working[0]; - $drives[$i]{'block-physical'} = $working[1]; - } + my @working = block_data($drives[$i]{'id'}); + $drives[$i]{'block-logical'} = $working[0]; + $drives[$i]{'block-physical'} = $working[1]; + } if ($block_type && @scsi && @by_id && ! -e "${working_path}model" && ! -e "${working_path}name"){ ## ok, ok, it's incomprehensible, search /dev/disk/by-id for a line that contains the # discovered disk name AND ends with the correct identifier, sdx @@ -8689,6 +8858,372 @@ sub dmesg_boot_data { return @data; } +sub smartctl_data { + eval $start if $b_log; + my (@data) = @_; + my ($b_attributes,$b_intel,$b_kingston,$cmd,%holder,$id,@working,@result,@split); + my ($splitter,$num,$a,$f,$r,$t,$v,$w,$y) = (':\s*',0,0,8,1,5,3,4,6); # $y is type, $t threashold, etc + my $smartctl = main::check_program('smartctl'); + for (my $i = 0; $i < scalar @data; $i++){ + next if !$data[$i]{'id'}; + ($b_attributes,$b_intel,$b_kingston,$splitter,$num,$a,$r) = (0,0,0,':\s*',0,0,1); + %holder = (); + #print $data[$i]{'id'},"\n"; + # m2 nvme failed on nvme0n1 drive id: + $id = $data[$i]{'id'}; + $id =~ s/n[0-9]+$// if $id =~ /^nvme/; + $cmd = "$smartctl -AHi /dev/" . $id . ' 2>/dev/null'; + @result = main::grabber("$cmd", '', 'strip'); + main::log_data('dump','@result', \@result) if $b_log; # log before cleanup + @result = grep {!/^(smartctl|Copyright|==)/} @result; + print 'Drive:/dev/' . $id . ":\n", Data::Dumper::Dumper\@result if $test[12]; + if (scalar @result < 4 ){ + if (grep {/failed: permission denied/i} @result){ + $data[$i]{'smart-permissions'} = main::row_defaults('smartctl-root'); + } + elsif (grep {/unknown usb bridge/i} @result){ + $data[$i]{'smart-error'} = main::row_defaults('smartctl-usb'); + } + elsif (grep {/A mandatory SMART command failed/i} @result){ + $data[$i]{'smart-error'} = main::row_defaults('smartctl-command-failed'); + } + else { + $data[$i]{'smart-error'} = main::row_defaults('smartctl-unknown'); + } + next; + } + else { + foreach my $row (@result){ + if ($row =~ /^ID#/){ + $splitter = '\s+'; + $b_attributes = 1; + $a = 1; + $r = 9; + next; + } + @split = split /$splitter/, $row; + next if !$b_attributes && ! defined $split[$r]; + # some cases where drive not in db threshhold will be: --- + # value is usually 0 padded which confuses perl. However this will + # make subsequent tests easier, and will strip off leading 0s + if ($b_attributes){ + $split[$t] = (main::is_numeric($split[$t])) ? int($split[$t]) : 0; + $split[$v] = (main::is_numeric($split[$v])) ? int($split[$v]) : 0; + } + ## DEVICE INFO ## + if ($split[$a] eq 'Device Model'){ + $b_intel = 1 if $split[$r] =~/\bintel\b/i; + $b_kingston = 1 if $split[$r] =~/kingston/i; + # usb/firewire/thunderbolt + if ($data[$i]{'type'}){ + @working = device_vendor("$split[$r]"); + $data[$i]{'drive-model'} = $working[1] if $data[$i]{'model'} && $data[$i]{'model'} ne $working[1]; + $data[$i]{'drive-vendor'} = $working[0] if $data[$i]{'vendor'} && $data[$i]{'vendor'} ne $working[0]; + } + } + elsif ($split[$a] eq 'Model Family'){ + @working = device_vendor("$split[$r]"); + $data[$i]{'family'} = $working[1]; + # $data[$i]{'family'} =~ s/$data[$i]{'vendor'}\s*// if $data[$i]{'vendor'}; + } + elsif ($split[$a] eq 'Firmware Version'){ + # 01.01A01 vs 1A01 + if ($data[$i]{'firmware'} && $split[$r] !~ /$data[$i]{'firmware'}/){ + $data[$i]{'drive-firmware'} = $split[$r]; + } + elsif (!$data[$i]{'firmware'}){ + $data[$i]{'firmware'} = $split[$r]; + } + } + elsif ($split[$a] eq 'Rotation Rate'){ + $data[$i]{'rotation'} = $split[$r] if $split[$r] !~ /^Solid/; + } + elsif ($split[$a] eq 'Serial Number'){ + if ( !$data[$i]{'serial'}){ + $data[$i]{'serial'} = $split[$r]; + } + elsif ($data[$i]{'type'} && $split[$r] ne $data[$i]{'serial'}){ + $data[$i]{'drive-serial'} = $split[$r]; + } + } + elsif ($split[$a] eq 'SATA Version is'){ + if ( $split[$r] =~ /SATA ([0-9.]+), ([0-9.]+ [^\s]+)( \(current: ([1-9.]+ [^\s]+)\))?/){ + $data[$i]{'sata'} = $1; + $data[$i]{'speed'} = $2 if !$data[$i]{'speed'}; + } + } + elsif ($split[$a] eq 'Sector Sizes'){ + if( $data[$i]{'type'} || !$data[$i]{'block-logical'} || !$data[$i]{'block-physical'} ){ + if ($split[$r] =~ m|^([0-9]+) bytes logical/physical| ){ + $data[$i]{'block-logical'} = $1; + $data[$i]{'block-physical'} = $1; + } + # 512 bytes logical, 4096 bytes physical + elsif ($split[$r] =~ m|^([0-9]+) bytes logical, ([0-9]+) bytes physical|){ + $data[$i]{'block-logical'} = $1; + $data[$i]{'block-physical'} = $2; + } + } + } + + ## SMART STATUS/HEALTH ## + elsif ($split[$a] eq 'SMART support is'){ + if ($split[$r] =~ /^(Available|Unavailable) /){ + $data[$i]{'smart'} = $1; + $data[$i]{'smart'} = ($data[$i]{'smart'} eq 'Unavailable') ? 'no' : 'yes'; + } + elsif ($split[$r] =~ /^(Enabled|Disabled)/ ){ + $data[$i]{'smart-support'} = lc($1); + } + } + elsif ($split[$a] eq 'SMART overall-health self-assessment test result' ){ + $data[$i]{'smart-status'} = $split[$r]; + # seen nvme that only report smart health, not smart support + $data[$i]{'smart'} = 'yes' if !$data[$i]{'smart'}; + } + + ## DEVICE CONDITION: temp/read/write/power on/cycles ## + # Attributes data fields, sometimes are same syntax as info block:... + elsif ( $split[$a] eq 'Power_Cycle_Count' || $split[$a] eq 'Power Cycles' ){ + $data[$i]{'smart-cycles'} = $split[$r] if $split[$r]; + } + elsif ($split[$a] eq 'Power_On_Hours' || $split[$a] eq 'Power On Hours' || + $split[$a] eq 'Power_On_Hours_and_Msec'){ + if ($split[$r]){ + $split[$r] =~ s/,//; + # trim off: h+0m+00.000s which is useless and at times empty anyway + $split[$r] =~ s/h\+.*$// if $split[$a] eq 'Power_On_Hours_and_Msec'; + # $split[$r] = 43; + if ($split[$r] =~ /^([0-9]+)$/){ + if ($1 > 9000){ + $data[$i]{'smart-power-on-hours'} = int($1/(24*365)) . 'y ' . int($1/24)%365 . 'd ' . $1%24 . 'h'; + } + elsif ($1 > 100){ + $data[$i]{'smart-power-on-hours'} = int($1/24) . 'd ' . $1%24 . 'h'; + } + else { + $data[$i]{'smart-power-on-hours'} = $split[$r] . ' hrs'; + } + } + else { + $data[$i]{'smart-power-on-hours'} = $split[$r]; + } + } + } + # 'Airflow_Temperature_Cel' like: 29 (Min/Max 14/43) so can't use -1 index + # Temperature like 29 Celsisu + elsif ( $split[$a] eq 'Temperature_Celsius' || $split[$a] eq 'Temperature' || + $split[$a] eq 'Airflow_Temperature_Cel' ) { + if (!$data[$i]{'temp'} && $split[$r]){ + $data[$i]{'temp'} = $split[$r]; + } + } + + ## DEVICE USE: Reads/Writes ## + elsif ($split[$a] eq 'Data Units Read'){ + $data[$i]{'smart-units-read'} = $split[$r]; + } + elsif ($split[$a] eq 'Data Units Written'){ + $data[$i]{'smart-units-written'} = $split[$r]; + } + elsif ($split[$a] eq 'Host_Reads_32MiB'){ + $split[$r] = $split[$r] * 32 * 1024; + $data[$i]{'smart-read'} = join ' ', main::get_size($split[$r]); + } + elsif ($split[$a] eq 'Host_Writes_32MiB'){ + $split[$r] = $split[$r] * 32 * 1024; + $data[$i]{'smart-written'} = join ' ', main::get_size($split[$r]); + } + elsif ($split[$a] eq 'Lifetime_Reads_GiB'){ + $data[$i]{'smart-read'} = $split[$r] . ' GiB'; + } + elsif ($split[$a] eq 'Lifetime_Writes_GiB'){ + $data[$i]{'smart-written'} = $split[$r] . ' GiB'; + } + elsif ($split[$a] eq 'Total_LBAs_Read'){ + if (main::is_numeric($split[$r])){ + # blocks in bytes, so convert to KiB, the internal unit here + # reports in 32MiB units, sigh + if ($b_intel){ + $split[$r] = $split[$r] * 32 * 1024; + } + # reports in 1 GiB units, sigh + elsif ($b_kingston){ + $split[$r] = $split[$r] * 1024 * 1024; + } + # this is what it's supposed to refer to + else { + $split[$r] = int($data[$i]{'block-logical'} * $split[$r] / 1024); + } + $data[$i]{'smart-read'} = join ' ', main::get_size($split[$r]); + } + } + elsif ($split[$a] eq 'Total_LBAs_Written'){ + if (main::is_numeric($split[$r])){ + # blocks in bytes, so convert to KiB, the internal unit here + # reports in 32MoB units, sigh + if ($b_intel){ + $split[$r] = $split[$r] * 32 * 1024; + } + # reports in 1 GiB units, sigh + elsif ($b_kingston){ + $split[$r] = $split[$r] * 1024 * 1024; + } + # this is what it's supposed to refer to, in byte blocks + else { + $split[$r] = int($data[$i]{'block-logical'} * $split[$r] / 1024); + } + $data[$i]{'smart-written'} = join ' ', main::get_size($split[$r]); + } + } + + ## DEVICE OLD AGE ## + # 191 G-Sense_Error_Rate 0x0032 001 001 000 Old_age Always - 291 + elsif ($split[$a] eq 'G-Sense_Error_Rate'){ + # $data[$i]{'smart-media-wearout'} = $split[$r]; + if ($b_attributes && $split[$r] > 100){ + $data[$i]{'smart-gsense-error-rate-r'} = $split[$r]; + } + } + elsif ($split[$a] eq 'Media_Wearout_Indicator'){ + # $data[$i]{'smart-media-wearout'} = $split[$r]; + if ($b_attributes && $split[$r] > 0){ + $data[$i]{'smart-media-wearout-v'} = $split[$v]; + $data[$i]{'smart-media-wearout-t'} = $split[$t]; + $data[$i]{'smart-media-wearout-f'} = $split[$f] if $split[$f] ne '-'; + } + } + elsif ($split[$a] eq 'Multi_Zone_Error_Rate'){ + # note: all t values are 0 that I have seen + if ( ($split[$v] - $split[$t]) < 50){ + $data[$i]{'smart-multizone-errors-v'} = $split[$v]; + $data[$i]{'smart-multizone-errors-t'} = $split[$v]; + } + + } + elsif ($split[$a] eq 'UDMA_CRC_Error_Count'){ + if (main::is_numeric($split[$r]) && $split[$r] > 50){ + $data[$i]{'smart-udma-crc-errors-r'} = $split[$r]; + $data[$i]{'smart-udma-crc-errors-f'} = main::row_defaults('smartctl-udma-crc') if $split[$r] > 500; + } + } + + ## DEVICE PRE-FAIL ## + elsif ($split[$a] eq 'Available_Reservd_Space'){ + # $data[$i]{'smart-available-reserved-space'} = $split[$r]; + if ($b_attributes && $split[$v] && $split[$t] && $split[$t]/$split[$v] > 0.92){ + $data[$i]{'smart-available-reserved-space-v'} = $split[$v]; + $data[$i]{'smart-available-reserved-space-t'} = $split[$t]; + $data[$i]{'smart-available-reserved-space-f'} = $split[$f] if $split[$f] ne '-'; + } + } + ## nvme splits these into two field/value sets + elsif ($split[$a] eq 'Available Spare'){ + $split[$r] =~ s/%$//; + $holder{'spare'} = int($split[$r]) if main::is_numeric($split[$r]); + } + elsif ($split[$a] eq 'Available Spare Threshold'){ + $split[$r] =~ s/%$//; + if ($holder{'spare'} && main::is_numeric($split[$r]) && $split[$r]/$holder{'spare'} > 0.92 ){ + $data[$i]{'smart-available-reserved-space-v'} = $holder{'spare'}; + $data[$i]{'smart-available-reserved-space-t'} = int($split[$r]); + } + } + elsif ($split[$a] eq 'End-to-End_Error'){ + if ($b_attributes && int($split[$r]) > 0 && $split[$t]){ + $data[$i]{'smart-end-to-end-v'} = $split[$v]; + $data[$i]{'smart-end-to-end-t'} = $split[$t]; + $data[$i]{'smart-end-to-end-f'} = $split[$f] if $split[$f] ne '-'; + } + } + # seen raw value: 0/8415644 + elsif ($split[$a] eq 'Raw_Read_Error_Rate'){ + if ($b_attributes && $split[$v] && $split[$t] && $split[$t]/$split[$v] > 0.92){ + $data[$i]{'smart-raw-read-error-rate-v'} = $split[$v]; + $data[$i]{'smart-raw-read-error-rate-t'} = $split[$t]; + $data[$i]{'smart-raw-read-error-rate-f'} = $split[$f] if $split[$f] ne '-'; + } + } + elsif ($split[$a] eq 'Reallocated_Sector_Ct'){ + if ($b_attributes && int($split[$r]) > 0 && $split[$t]){ + $data[$i]{'smart-reallocated-sectors-v'} = $split[$v]; + $data[$i]{'smart-reallocated-sectors-t'} = $split[$t]; + $data[$i]{'smart-reallocated-sectors-f'} = $split[$f] if $split[$f] ne '-'; + } + } + elsif ($split[$a] eq 'Retired_Block_Count'){ + if ($b_attributes && int($split[$r]) > 0 && $split[$t]){ + $data[$i]{'smart-retired-blocks-v'} = $split[$v]; + $data[$i]{'smart-retired-blocks-t'} = $split[$t]; + $data[$i]{'smart-retired-blocks-f'} = $split[$f] if $split[$f] ne '-'; + } + } + elsif ($split[$a] eq 'Runtime_Bad_Block'){ + if ($b_attributes && $split[$v] && $split[$t] && $split[$t]/$split[$v] > 0.92 ){ + $data[$i]{'smart-runtime-bad-block-v'} = $split[$v]; + $data[$i]{'smart-runtime-bad-block-t'} = $split[$t]; + $data[$i]{'smart-runtime-bad-block-f'} = $split[$f] if $split[$f] ne '-'; + } + } + elsif ($split[$a] eq 'Seek_Error_Rate'){ + # value 72; threshold either 000 or 30 + if ($b_attributes && $split[$v] && $split[$t] && $split[$t]/$split[$v] > 0.92 ){ + $data[$i]{'smart-seek-error-rate-v'} = $split[$v]; + $data[$i]{'smart-seek-error-rate-t'} = $split[$t]; + $data[$i]{'smart-seek-error-rate-f'} = $split[$f] if $split[$f] ne '-'; + } + } + elsif ($split[$a] eq 'Spin_Up_Time'){ + # raw will always be > 0 on spinning disks + if ($b_attributes && $split[$v] && $split[$t] && $split[$t]/$split[$v] > 0.92 ){ + $data[$i]{'smart-spinup-time-v'} = $split[$v]; + $data[$i]{'smart-spinup-time-t'} = $split[$t]; + $data[$i]{'smart-spinup-time-f'} = $split[$f] if $split[$f] ne '-'; + } + } + elsif ($split[$a] eq 'SSD_Life_Left'){ + # raw will always be > 0 on spinning disks + if ($b_attributes && $split[$v] && $split[$t] && $split[$t]/$split[$v] > 0.92 ){ + $data[$i]{'smart-ssd-life-left-v'} = $split[$v]; + $data[$i]{'smart-ssd-life-left-t'} = $split[$t]; + $data[$i]{'smart-ssd-life-left-f'} = $split[$f] if $split[$f] ne '-'; + } + } + elsif ($split[$a] eq 'Unused_Rsvd_Blk_Cnt_Tot'){ + # raw will always be > 0 on spinning disks + if ($b_attributes && $split[$v] && $split[$t] && $split[$t]/$split[$v] > 0.92 ){ + $data[$i]{'smart-unused-reserve-block-v'} = $split[$v]; + $data[$i]{'smart-unused-reserve-block-t'} = $split[$t]; + $data[$i]{'smart-unused-reserve-block-f'} = $split[$f] if $split[$f] ne '-'; + } + } + elsif ($split[$a] eq 'Used_Rsvd_Blk_Cnt_Tot'){ + # raw will always be > 0 on spinning disks + if ($b_attributes && $split[$v] && $split[$t] && $split[$t]/$split[$v] > 0.92 ){ + $data[$i]{'smart-used-reserve-block-v'} = $split[$v]; + $data[$i]{'smart-used-reserve-block-t'} = $split[$t]; + $data[$i]{'smart-used-reserve-block-f'} = $split[$f] if $split[$f] ne '-'; + } + } + elsif ($b_attributes ){ + if ( $split[$y] eq 'Pre-fail' && ($split[$f] ne '-' || + ($split[$t] && $split[$v] && $split[$t]/$split[$v] > 0.92 ))) { + $num++; + $data[$i]{'smart-unknown-' . $num . '-a'} = $split[$a]; + $data[$i]{'smart-unknown-' . $num . '-v'} = $split[$v]; + $data[$i]{'smart-unknown-' . $num . '-w'} = $split[$v]; + $data[$i]{'smart-unknown-' . $num . '-t'} = $split[$t]; + $data[$i]{'smart-unknown-' . $num . '-f'} = $split[$f] if $split[$f] ne '-'; + } + } + } + } + } + #print Data::Dumper::Dumper\@data; + eval $end if $b_log; + return @data; +} + # check for usb/firewire/[and thunderwire when data found] sub peripheral_data { eval $start if $b_log; @@ -8702,7 +9237,7 @@ sub peripheral_data { if (/usb-/i){ $type = 'USB'; } - elsif (/ieee1394--/i){ + elsif (/ieee1394-/i){ $type = 'FireWire'; } last; @@ -8866,32 +9401,32 @@ sub device_vendor { ['(^MKN|Mushkin)','Mushkin','Mushkin',''], # MKNS # MU = Multiple_Flash_Reader too risky: |M[UZ][^L] HD103SI HD start risky # HM320II HM320II - ['(SAMSUNG|^MCG[0-9]+GC|^MCC|^MCB0E|^[GS]2 Portable|^DUO\b|^P3|^(HM|SP)[0-9]{2}|^MZMPC|^HD[0-9]{3}[A-Z]{2}$)','SAMSUNG','Samsung',''], # maybe ^SM, ^HM + ['(SAMSUNG|^MCG[0-9]+GC|^MCC|^MCBOE|^[GS]2 Portable|^D3 Station|^DUO\b|^P3|^(HM|SP)[0-9]{2}|^MZMPC|^HD[0-9]{3}[A-Z]{2}$)','SAMSUNG','Samsung',''], # maybe ^SM, ^HM # Android UMS Composite? ['(SanDisk|^SDS[S]?[DQ]|^SL([0-9]+)G|^AFGCE|^U3\b|ULTRA\sFIT|Clip Sport|Cruzer|^Extreme)','SanDisk','SanDisk',''], ['^STEC\b','^STEC\b','STEC',''], # ssd drive, must come before seagate ST test # real, SSEAGATE Backup+; XP1600HE30002 | 024 HN (spinpoint) - ['(^ST[^T]|[S]?SEAGATE|^X[AFP]|^5AS|^BUP|Expansion Desk|FreeAgent|GoFlex|Backup(\+|\s?Plus)\s?(Hub)?|OneTouch)','[S]?SEAGATE','Seagate',''], + ['(^ST[^T]|[S]?SEAGATE|^X[AFP]|^5AS|^BUP|Expansion Desk|^Expansion|FreeAgent|GoFlex|Backup(\+|\s?Plus)\s?(Hub)?|OneTouch)','[S]?SEAGATE','Seagate',''], ['^(WD|WL[0]9]|Western Digital|My (Book|Passport)|\d*LPCX|Elements|easystore|MD0|M000|EARX|EFRX|\d*EAVS|0JD|JPVX|[0-9]+(BEV|(00)?AAK|AAV|AZL|EA[CD]S))','(^WDC|Western\s?Digital)','Western Digital',''], ## Then better known ones ## ['^(A-DATA|ADATA|AXN|CH11|HV[1-9]|IM2)','^(A-DATA|ADATA)','A-Data',''], ['^ADTRON','^(ADTRON)','Adtron',''], ['^ASUS','^ASUS','ASUS',''], # ATCS05 can be hitachi travelstar but not sure - ['^ATP','^ATP[\s\-]','ATP',''], + ['^ATP','^ATP[\s-]','ATP',''], # Force MP500 - ['^(Corsair|Force\s|(Flash\s*)?Voyager)','^Corsair','Corsair',''], + ['^(Corsair|Force\s|(Flash\s*)?(Survivor|Voyager))','^Corsair','Corsair',''], ['^(FUJITSU|MJA|MH[TVWYZ][0-9]|MP|MAP[0-9])','^FUJITSU','Fujitsu',''], # note: 2012: wdc bought hgst ['^(HGST|Touro|5450)','^HGST','HGST (Hitachi)',''], # HGST HUA - ['^(Hitachi|HD[PST]|DK[0-9]|IC|HT|HU)','^Hitachi','Hitachi',''], + ['^(Hitachi|HCS|HD[PST]|DK[0-9]|IC|HT|HU)','^Hitachi','Hitachi',''], # vb: VB0250EAVER but clashes with vbox; HP_SSD_S700_120G ;GB0500EAFYL GB starter too generic? # DX110064A5xnNMRI ids as HP and Sandisc, same ID, made by sandisc for hp? not sure - ['^(HP\b|MB[0-6]|G[BJ][01]|v[0-9]{3}[bgorw]$|x[0-9]{3}[w]$)','^HP','HP',''], + ['^(HP\b|[MV]B[0-6]|G[BJ][01]|DF[012]|v[0-9]{3}[bgorw]$|x[0-9]{3}[w]$)','^HP','HP',''], ['^(LSD|Lexar|JumpDrive|JD\s?Firefly)','^Lexar','Lexar',''], # mmc-LEXAR_0xb016546c; JD Firefly; # OCZSSD2-2VTXE120G is OCZ-VERTEX2_3.5 - ['^(OCZ|APOC|D2|DEN|DEN|DRSAK|EC188|FTNC|GFGC|MANG|MMOC|NIMC|NIMR|PSIR|RALLY2|TALOS2|TMSC|TRSAK)','^OCZ[\s\-]','OCZ',''], - ['^OWC','^OWC[\s\-]','OWC',''], + ['^(OCZ|APOC|D2|DEN|DEN|DRSAK|EC188|FTNC|GFGC|MANG|MMOC|NIMC|NIMR|PSIR|RALLY2|TALOS2|TMSC|TRSAK)','^OCZ[\s-]','OCZ',''], + ['^OWC','^OWC[\s-]','OWC',''], ['^Philips','^Philips','Philips',''], ['^PIONEER','^PIONEER','Pioneer',''], ['^PNY','^PNY\s','PNY','','^PNY'], @@ -8918,6 +9453,7 @@ sub device_vendor { ['^BHT','^BHT','BHT',''], ['^BIOSTAR','^BIOSTAR','Biostar',''], ['^BIWIN','^BIWIN','BIWIN',''], + ['^Braveeagle','^Braveeagle','BraveEagle',''], ['^(BUFFALO|BSC)','^BUFFALO','Buffalo',''], # usb: BSCR05TU2 ['^Bulldozer','^Bulldozer','Bulldozer',''], ['^Centerm','^Centerm','Centerm',''], @@ -8939,6 +9475,7 @@ sub device_vendor { ['^(Eaget|V8$)','^Eaget','Eaget',''], ['^EDGE','^EDGE','EDGE',''], ['^Elecom','^Elecom','Elecom',''], + ['^Emtec','^Emtec','Emtec',''], ['^EXCELSTOR','^EXCELSTOR( TECHNO(LOGY)?)?','ExcelStor',''], ['^EZLINK','^EZLINK','EZLINK',''], ['^Fantom','^Fantom( Drive[s]?)?','Fantom Drives',''], @@ -8975,6 +9512,7 @@ sub device_vendor { ['^(Intenso|(Alu|Basic|Business|Micro|Mobile|Rainbow|Speed|Twister) Line|Rainbow)','^Intenso','Intenso',''], ['^(Iomega|ZIP\b)','^Iomega','Iomega',''], ['^JingX','^JingX','JingX',''], #JingX 120G SSD - not confirmed, but guessing + ['^Jingyi','^Jingyi','Jingyi',''], # NOTE: ITY2 120GB hard to find ['^JMicron','^JMicron(\s?Tech(nology)?)?','JMicron Tech',''], #JMicron H/W raid ['^KingDian','^KingDian','KingDian',''], @@ -8991,8 +9529,8 @@ sub device_vendor { ['^LEN','^Lenovo','Lenovo',''], ['^RPFT','','Lenovo O.E.M.',''], ['^LG\b','^LG','LG',''], - ['^(LITE[\-\s]?ON[\s\-]?IT)','^LITE[\-]?ON[\s\-]?IT','LITE-ON IT',''], # LITEONIT_LSS-24L6G - ['^(LITE[\-\s]?ON|PH[1-9])','^LITE[\-]?ON','LITE-ON',''], # PH6-CE240-L + ['^(LITE[-\s]?ON[\s-]?IT)','^LITE[-]?ON[\s-]?IT','LITE-ON IT',''], # LITEONIT_LSS-24L6G + ['^(LITE[-\s]?ON|PH[1-9])','^LITE[-]?ON','LITE-ON',''], # PH6-CE240-L ['^LONDISK','^LONDISK','LONDISK',''], ['^M-Systems','^M-Systems','M-Systems',''], ['^(Mach\s*Xtreme|MXSSD)','^Mach\s*Xtreme','Mach Xtreme',''], @@ -9007,13 +9545,19 @@ sub device_vendor { ['^MD[1-9]','^Max\s*Digital','MaxDigital',''], ['^Medion','^Medion','Medion',''], ['^(MEDIAMAX|WL[0-9]{2})','^MEDIAMAX','MediaMax',''], + ['^Mengmi','^Mengmi','Mengmi',''], + ['^Miracle','^Miracle','Miracle',''], ['^Monster\s?Digital','^Monster\s?Digital','Monster Digital',''], ['^Morebeck','^Morebeck','Morebeck',''], ['^Motorola','^Motorola','Motorola',''], + ['^Moweek','^Moweek','Moweek',''], + #MRMAD4B128GC9M2C + ['^(MRMA|Memoright)','^Memoright','Memoright',''], ['^MTRON','^MTRON','MTRON',''], ['^Netac','^Netac','Netac',''], ['^OOS[1-9]','','Utania',''], ['^OWC','^OWC\b','OWC',''], + ['^oyunkey','^oyunkey','Oyunkey',''], ['^PALIT','PALIT','Palit',''], # ssd ['^PERC\b','','Dell PowerEdge RAID Card',''], # ssd ['^(PS[8F]|Patriot)','^Patriot([-\s]?Memory)?','Patriot',''], @@ -9028,8 +9572,12 @@ sub device_vendor { ['^RENICE','^RENICE','Renice',''], ['^(Ricoh|R5)','^Ricoh','Ricoh',''], ['^RIM[\s]','^RIM','RIM',''], + #RTDMA008RAV2BWL comes with lenovo but don't know brand ['^Runcore','^Runcore','Runcore',''], + ['^Sabrent','^Sabrent','Sabrent',''], ['^Sage','^Sage(\s?Micro)?','Sage Micro',''], + ['^SandForce','^SandForce','SandForce',''], + ['^Sannobel','^Sannobel','Sannobel',''], ['^SigmaTel','^SigmaTel','SigmaTel',''], # DIAMOND_040_GB ['^(SILICON\s?MOTION|SM[0-9])','^SILICON\s?MOTION','Silicon Motion',''], @@ -9058,6 +9606,7 @@ sub device_vendor { ['^TEAM','^TEAM( Group)?','Team',''], ['^Teclast','^Teclast','Teclast',''], ['^Teleplan','^Teleplan','Teleplan',''], + ['^TEUTONS','^TEUTONS','TEUTONS',''], ['^Tigo','^Tigo','Tigo',''], ['^TopSunligt','^TopSunligt','TopSunligt',''], # is this a typo? hard to know ['^TopSunlight','^TopSunlight','TopSunlight',''], @@ -9073,6 +9622,7 @@ sub device_vendor { ['^VMware','^VMware','VMware',''], ['^(Vseky|Vaseky)','^Vaseky','Vaseky',''], # ata-Vseky_V880_350G_ ['^(YUCUN|R880)','^YUCUN','YUCUN',''], + ['^ZEUSLAP','^ZEUSLAP','ZEUSLAP',''], ['^(Zheino|CHN[0-9])','^Zheino','Zheino',''], ['^ZSPEED','^ZSPEED','ZSpeed',''], ['^ZTC','^ZTC','ZTC',''], @@ -9090,7 +9640,7 @@ sub device_vendor { $model = 'N/A'; } } - $model =~ s/^[\s\-_]+|[\s\-_]+$//g; + $model =~ s/^[\[\s_-]+|[\s\-_-]+$//g; $model =~ s/\s\s/ /g; @data = ($vendor,$model); last; @@ -9119,6 +9669,7 @@ sub hdd_temp { @data = main::grabber("$sudo$nvme smart-log $device 2>/dev/null"); foreach (@data){ my @row = split /\s*:\s*/, $_; + next if !$row[0]; # other rows may have: Temperature sensor 1 : if ( $row[0] eq 'temperature') { $row[1] =~ s/\s*C//; @@ -9143,7 +9694,7 @@ sub hdd_temp { return $hdd_temp; } # args: 1: block id -sub admin_data { +sub block_data { eval $start if $b_log; my ($id) = @_; # 0: logical block size 1: disk physical block size/partition block size; @@ -9730,6 +10281,7 @@ sub x_drivers { # $log = "$ENV{HOME}/bin/scripts/inxi/data/xorg-logs/loading-unload-failed-all41-mint.txt"; # $log = "$ENV{HOME}/bin/scripts/inxi/data/xorg-logs/loading-unload-failed-phd21-mint.txt"; # $log = "$ENV{HOME}/bin/scripts/inxi/data/xorg-logs/Xorg.0-gm10.log"; + # $log = "$ENV{HOME}/bin/scripts/inxi/data/xorg-logs/xorg-multi-driver-1.log"; my @xorg = main::reader($log); # list is from sgfxi plus non-free drivers, plus ARM drivers my $list = join '|',qw(amdgpu apm ark armsoc atimisc ati @@ -9897,6 +10449,7 @@ sub display_compositor { ['orbital','orbital','','orbital'], ['papyros','papyros','','papyros'], ['perceptia','perceptia','','perceptia'], + ['picom','picom','','picom'], ['rustland','rustland','','rustland'], ['sommelier','sommelier','','sommelier'], ['sway','sway','','sway'], @@ -11050,7 +11603,7 @@ sub wan_ip { } } } - if ($ip && $show{'filter'}){ + if ($ip && $use{'filter'}){ $ip = $filter_string; } $ip ||= main::row_defaults('IP', 'WAN IP'); @@ -11717,7 +12270,7 @@ sub partition_data { } } $id = join ' ', @row[$cols .. $#row]; - $id =~ s/\/home\/[^\/]+\/(.*)/\/home\/$filter_string\/$1/ if $show{'filter'}; + $id =~ s/\/home\/[^\/]+\/(.*)/\/home\/$filter_string\/$1/ if $use{'filter'}; $size = $row[$cols - $back_size]; if ($b_admin && -e "/sys/block/"){ @working = admin_data($blockdev,$dev_base,$size); @@ -15717,7 +16270,7 @@ sub create_output { } } if ($extra > 2){ - if (!$show{'filter'}){ + if (!$use{'filter'}){ $rows[0]{main::key($num++,'Location')} = complete_location($location[1],$weather{'city'},$weather{'state'},$weather{'country'}); if ($weather{'elevation-m'} || $weather{'elevation-ft'}){ $rows[0]{main::key($num++,'altitude')} = elevation_output($weather{'elevation-m'},$weather{'elevation-ft'}); @@ -16240,7 +16793,7 @@ sub get_location { sub complete_location { eval $start if $b_log; my ($location,$city,$state,$country) = @_; - if ($location && $location =~ /[\+\-0-9]/ && $city){ + if ($location && $location =~ /[0-9+-]/ && $city){ $location = $country . ', ' . $location if $country && $location !~ m|$country|i; $location = $state . ', ' . $location if $state && $location !~ m|$state|i; $location = $city . ', ' . $location if $city && $location !~ m|$city|i; @@ -16449,7 +17002,7 @@ sub get_env_de_data { if (!$desktop[0]){ # 1 equals 1/0; 2 env var search; 3 values; 4 version; 5 - gtk tk; 6 - qt tk my @desktops =( - [1,'unity','unity','cinnamon',0,0], + [1,'unity','unity','unity',0,0], [0,'budgie','budgie','budgie-desktop',0,0], # debian package: lxde-core. # NOTE: some distros fail to set XDG data for root @@ -18672,7 +19225,7 @@ sub soc_type { my ($type,$info,$driver) = @_; # I2S or i2s. I2C is i2 controller |[iI]2[Ss]. note: odroid hdmi item is sound only # snd_soc_dummy. simple-audio-amplifier driver: speaker_amp - if ($type =~ /^(daudio|.*hifi.*|.*sound[\-_]card|.*dac[0-9]?)$/ || + if ($type =~ /^(daudio|.*hifi.*|.*sound[_-]card|.*dac[0-9]?)$/ || ($info && $info !~ /amp|codec|dummy/ && $info =~ /(sound|audio)/) || ($driver && $driver !~ /(codec|dummy)/ && $driver =~ /(audio|snd|sound)/) ){ $type = 'audio'; @@ -19116,7 +19669,7 @@ sub set_ps_gui { @temp=qw(3dwm asc budgie-wm compiz compton deepin-wm dwc dcompmgr enlightenment fireplace gnome-shell grefson kmscon kwin_wayland kwin_x11 liri marco metisse mir moblin motorcar muffin mutter - orbital papyros perceptia rustland sommelier sway swc + orbital papyros perceptia picom rustland sommelier sway swc ukwm unagi unity-system-compositor wavy waycooler way-cooler wayhouse westford weston xcompmgr); @match = (@match,@temp); @@ -20080,7 +20633,7 @@ sub generate_system_data { } # don't print the desktop if it's a wm and the same if ($extra > 1 && $desktop_data[5] && - (!$desktop_data[0] || $desktop_data[5] =~ /^(deepin.+|gnome[\s\-_]shell|budgie.+)$/i || + (!$desktop_data[0] || $desktop_data[5] =~ /^(deepin.+|gnome[\s_-]shell|budgie.+)$/i || index(lc($desktop_data[5]),lc($desktop_data[0])) == -1 )){ $wm = $desktop_data[5]; $wm .= ' ' . $desktop_data[6] if $extra > 2 && $desktop_data[6]; diff --git a/inxi.1 b/inxi.1 index 22b3677..09dd819 100644 --- a/inxi.1 +++ b/inxi.1 @@ -1,4 +1,4 @@ -.TH INXI 1 "2019\-11\-19" inxi "inxi manual" +.TH INXI 1 "2020\-03\-14" inxi "inxi manual" .SH NAME inxi \- Command line system information script for console and IRC .SH SYNOPSIS @@ -160,7 +160,8 @@ Also, unmounted partitions are not counted in disk use percentages since inxi has no access to the used amount. Also shows per disk information: Disk ID, type (if present), vendor (if detected), -model, and size. See \fBExtra Data Options\fR for more features. +model, and size. See \fBExtra Data Options\fR (\fB\-x\fR options) and +\fBAdmin Extra Data Options\fR (\fB\-\-admin\fR options) for many more features. .TP .B \-f\fR,\fB \-\-flags\fR Show all CPU flags used, not just the short list. Not shown with \fB\-F\fR in order @@ -526,7 +527,8 @@ actual widths of the terminal. \fB80\fR is the minimum width supported. .TP .B \-z\fR,\fB \-\-filter\fR Adds security filters for IP addresses, serial numbers, MAC, -location (\fB\-w\fR), and user home directory name. On by default for IRC clients. +location (\fB\-w\fR), and user home directory name. Removes Host:. +On by default for IRC clients. .TP .B \-Z\fR,\fB \-\-filter\-override\fR Absolute override for output filters. Useful for debugging networking @@ -878,7 +880,10 @@ weather observation time (if available), sunset/sunrise (if available). These options are triggered with \fB\-\-admin\fR or \fB\-a\fR. Admin options are advanced output options, and are more technical, and mostly of interest to system administrators or other machine admins. -The \fB\-\-admin\fR option only has to be used once, and will trigger the following features. + +The \fB\-\-admin\fR option sets \fB\-xxx\fR, and only has to be used once. +It will trigger the following features: + .TP .B \-a \-C\fR \- Adds CPU family, model\-id, and stepping (replaces \fBrev\fR of \fB\-Cx\fR). @@ -894,6 +899,27 @@ Format is \fBhexadecimal (decimal)\fR if greater than 9, otherwise \fBhexadecima .B \-a \-d\fR,\fB\-a \-D\fR \- Adds logical and physical block size in bytes. +Using \fBsmartctl\fR (requires sudo/root privileges). + +\- Adds device model family, like \fBCaviar Black\fR, if available. + +\- Adds SATA type (eg 1.0, 2.6, 3.0) if a SATA device. + +\- Adds SMART report line: status, enabled/disabled, health, powered on, +cycles, and some error cases if out of range values. Note that for Pre-fail items, +it will show the VALUE and THRESHOLD numbers. It will also fall back for unknown +attributes that are or have been failing and print out the Attribute name, value, +threshold, and failing message. This way even for unhandled Attribute names, +you should get a solid report for full failure cases. Other cases may show +if inxi believes that the item may be approaching failure. This is a guess so +make sure to check the drive and smartctl full output to verify before +taking any further action. + +\- Adds, for USB or other external drives, actual model name/serial if +available, and different from enclosure model/serial, and corrects block +sizes if necessary. Adds in drive temperature for some drives as well, +and other useful data. + .TP .B \-a \-p\fR,\fB\-a \-P\fR \- Adds raw partition size, including file system overhead, partition table, e.g. @@ -979,7 +1005,10 @@ Force inxi to use Curl, Fetch, Perl, or Wget for downloads. .B \-\-host\fR Turns on hostname in System line. Overrides inxi config file value (if set): -\fBSHOW_HOST='false'\fR +\fBSHOW_HOST='false'\fR \- Same as: \fBSHOW_HOST='true'\fR + +This is an absolute override, the host will always show no matter what +other switches you use. .TP .B \-\-indent\-min [integer]\fR @@ -1000,11 +1029,14 @@ Updates / installs man page with \fB\-U\fR if \fBpinxi\fR or using \fB\-U 3\fR d .TP .B \-\-no\-host\fR -Turns off hostname in System line. Useful, in combination with \fB\-z\fR, -for anonymizing inxi output for posting on forums or IRC. Same as -configuration value: +Turns off hostname in System line. This is default when using \fB\-z\fR, +for anonymizing inxi output for posting on forums or IRC. Overrides +configuration value (if set): -\fBSHOW_HOST='false'\fR +\fBSHOW_HOST='true'\fR \- Same as: \fBSHOW_HOST='false'\fR + +This is an absolute override, the host will not show no matter what other +switches you use. .TP .B \-\-no\-man\fR diff --git a/inxi.changelog b/inxi.changelog index e6efbde..2488c4e 100644 --- a/inxi.changelog +++ b/inxi.changelog @@ -1,3 +1,58 @@ +===================================================================================== +Version: 3.0.38 +Patch: 00 +Date: 2020-03-14 +----------------------------------- +Changes: +----------------------------------- + +Bugs: +1. Fixed undefined error that could happen, in rare cases, in hdd_temp logic. + +Fixes: +1. Fixed Elbrus cpu nazming, model 9 is 8CV, not 8CB (Cyrillic error) +2. Preventitive, was not using '-' quite correctly in all regex ranges. +3. Had wrong desktop string listed in Unity +4. Reordered Family/Drive model in usb drive reports, it's to make it +more obvious what is what. +5. Adjusted indexing of splits to get better results in corner cases. +6. Fixed some numbering issues. +7. Added trimming n1 from nvme0 type names for nvme, this corrects some +issues users were having. +8. Fixed a division by 0 error in smartctl data grabber. +9. Fixed a Perl issue, didn't realize perl treats 000 as a string, not 0. +10. Another Perl fix, int() only wants to get numeric values sent to it, +I'd assumed a different behavior, non numerics get converted to 0, but that's +not how Perl sees things. Things like this, by the way, are why Perl is so +absurdly fast. + +Enhancements: +1. More disk vendors. The list will never be complete!! We have found eternal +churn!! Thanks to linux lite hardware database as always. +2. Big one!!! Now inxi uses smartctl data, if installed, for getting advanced +drive information (with -a). See man and help for details. Will show failing drives, +etc. Lots of info can be available, but sometimes data is not in smartctl db, +so inxi can't find it, that's not an inxi bug, it's just how it is. +3. Made hours on more human readable, into days/hours, for -a smartctl disk +report. +4. Added $test[12] for smartctl data printout, and $test[13] for disk array print out. +Note that advanced debugger outputs can change or vary depending on what is being +worked on so don't in general rely on these always being around. But they do +tend to say stuck in place once I add them. +5. Added some nvme stuff, spare reserve, if you need it, you'll appreciate it, +if not, you'll never know it's there. +6. By request from some forum issue thread: made --host only be shown onif not +--filter or not --host. This makes -z remove hostname, but retains ability to +do absolute overrides. Hostname should have always been filtered out like that, +it was an oversight. I think that was Manjaro who asked that, but I forget. +Note that this change, as usual, will not alter expected behaviors if users +have config item for hostname set. +7. Added support for picom compositor, thanks user codebling for that, I think +that's compiz fork, the real branch that is that is being developed. + +----------------------------------- +-- Harald Hope - Sat, 14 Mar 2020 22:56:32 -0700 + ===================================================================================== Version: 3.0.37 Patch: 00