diff --git a/inxi b/inxi index 99c469a..fd37432 100755 --- a/inxi +++ b/inxi @@ -1,6 +1,6 @@ #!/usr/bin/env perl ## infobash: Copyright (C) 2005-2007 Michiel de Boer aka locsmif -## inxi: Copyright (C) 2008-2018 Harald Hope +## inxi: Copyright (C) 2008-2019 Harald Hope ## Additional features (C) Scott Rogers - kde, cpu info ## Further fixes (listed as known): Horst Tritremmel ## Steven Barrett (aka: damentz) - usb audio patch; swap percent used patch @@ -31,8 +31,8 @@ use POSIX qw(uname strftime ttyname); ## INXI INFO ## my $self_name='inxi'; -my $self_version='3.0.30'; -my $self_date='2018-12-31'; +my $self_version='3.0.31'; +my $self_date='2019-02-06'; my $self_patch='00'; ## END INXI INFO ## @@ -72,7 +72,7 @@ $b_fake_bsd,$b_fake_dboot,$b_fake_dmidecode,$b_fake_pciconf,$b_fake_sysctl, $b_fake_usbdevs,$b_force_display,$b_gpudata,$b_irc, $b_log,$b_log_colors,$b_log_full,$b_man,$b_mem,$b_mips, $b_pci,$b_pci_tool,$b_ppc,$b_proc_partitions,$b_ps_gui, -$b_root,$b_running_in_display, +$b_root,$b_running_in_display,$b_skip_dig, $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 @@ -81,11 +81,11 @@ $b_label_uuid,$b_lsblk,$b_partitions,$b_raid); my ($b_sysctl_disk,$b_update,$b_weather) = (1,1,1); ## System -my ($bsd_type,$language,$os,$pci_tool,$device_vm) = ('','','','',''); +my ($bsd_type,$device_vm,$language,$os,$pci_tool,$wan_url) = ('','','','','',''); my ($bits_sys,$cpu_arch); my ($cpu_sleep,$dl_timeout,$limit,$ps_cols,$ps_count) = (0.35,4,10,0,5); my $sensors_cpu_nu = 0; -my $weather_unit='mi'; +my ($dl_ua,$weather_source,$weather_unit) = ('s-tools/' . $self_name . '-',100,'mi'); ## Tools my ($display,$ftp_alt,$tty_session); @@ -218,7 +218,7 @@ sub check_tools { 'dmidecode' => { 'action' => $action, 'missing' => 'Required program dmidecode not available', - 'permissions' => 'Unable to run dmidecode. Are you root?', + 'permissions' => 'Unable to run dmidecode. Root privileges required.', 'smbios' => 'No SMBIOS data for dmidecode to process', 'no-data' => 'dmidecode is not allowed to read /dev/mem', 'unknown-error' => 'dmidecode was unable to generate data', @@ -328,7 +328,6 @@ sub check_tools { } set_fake_tools() if $b_fake_bsd; } - # args: 1 - desktop/app command for --version; 2 - search string; # 3 - space print number; 4 - [optional] version arg: -v, version, etc # 5 - [optional] exit first find 0/1; 6 - [optional] 0/1 stderr output @@ -343,7 +342,7 @@ sub set_basics { $b_irc = ( check_program('tty') && system('tty >/dev/null') ) ? 1 : 0; # print "birc: $b_irc\n"; $b_display = ( $ENV{'DISPLAY'} ) ? 1 : 0; - $b_root = ( $ENV{'HOME'} eq '/root' ) ? 1 : 0; + $b_root = $< == 0; # root UID 0, all others > 0 $dl{'dl'} = 'curl'; $dl{'curl'} = 1; $dl{'tiny'} = 1; # note: two modules needed, tested for in set_downloader @@ -1084,6 +1083,13 @@ sub get_config_item { 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 'USB_SYS') {$b_usb_sys = $val if is_int($val)} + elsif ($key eq 'WAN_IP_URL') { + if ($val =~ /^(ht|f)tp[s]?:\//i){ + $wan_url = $val; + $b_skip_dig = 1; + } + } + elsif ($key eq 'WEATHER_SOURCE') {$weather_source = $val if is_int($val)} elsif ($key eq 'WEATHER_UNIT') { $val = lc($val) if $val; if ($val && $val =~ /^(c|f|cf|fc|i|m|im|mi)$/){ @@ -2152,10 +2158,11 @@ sub user_debug_test_1 { #### ------------------------------------------------------------------- sub download_file { - my ($type, $url, $file) = @_; + my ($type, $url, $file,$ua) = @_; my ($cmd,$args,$timeout) = ('','',''); my $debug_data = ''; my $result = 1; + $ua = ($ua && $dl{'ua'}) ? $dl{'ua'} . $ua : ''; $dl{'no-ssl-opt'} ||= ''; $dl{'spider'} ||= ''; $file ||= 'N/A'; # to avoid debug error @@ -2176,22 +2183,23 @@ sub download_file { } # But: 0 is success, and 1 is false for these # when strings are returned, they will be taken as true + # urls must be " quoted in case special characters present else { if ($type eq 'stdout'){ $args = $dl{'stdout'}; - $cmd = "$dl{'dl'} $dl{'no-ssl-opt'} $timeout $args $url $dl{'null'}"; + $cmd = "$dl{'dl'} $dl{'no-ssl-opt'} $ua $timeout $args \"$url\" $dl{'null'}"; $result = qx($cmd); $debug_data = ($result) ? 'Success: stdout data not null.' : 'Download resulted in null data!'; } elsif ($type eq 'file') { $args = $dl{'file'}; - $cmd = "$dl{'dl'} $dl{'no-ssl-opt'} $timeout $args $file $url $dl{'null'}"; + $cmd = "$dl{'dl'} $dl{'no-ssl-opt'} $ua $timeout $args $file \"$url\" $dl{'null'}"; system($cmd); $result = ($?) ? 0 : 1; # reverse these into Perl t/f $debug_data = $result; } elsif ( $dl{'dl'} eq 'wget' && $type eq 'spider'){ - $cmd = "$dl{'dl'} $dl{'no-ssl-opt'} $timeout $dl{'spider'} $url"; + $cmd = "$dl{'dl'} $dl{'no-ssl-opt'} $ua $timeout $dl{'spider'} \"$url\""; system($cmd); $result = ($?) ? 0 : 1; # reverse these into Perl t/f $debug_data = $result; @@ -2247,6 +2255,7 @@ sub get_file { sub set_downloader { eval $start if $b_log; + my $quiet = ''; $dl{'no-ssl'} = ''; $dl{'null'} = ''; $dl{'spider'} = ''; @@ -2271,25 +2280,30 @@ sub set_downloader { $dl{'timeout'} = ''; } elsif ( $dl{'curl'} && check_program('curl') ){ + $quiet = '-s ' if !$test[1]; $dl{'dl'} = 'curl'; - $dl{'file'} = ' -L -s -o '; + $dl{'file'} = " -L ${quiet}-o "; $dl{'no-ssl'} = ' --insecure'; - $dl{'stdout'} = ' -L -s '; + $dl{'stdout'} = " -L ${quiet}"; $dl{'timeout'} = ' -y '; + $dl{'ua'} = ' -A ' . $dl_ua; } elsif ($dl{'wget'} && check_program('wget') ){ + $quiet = '-q ' if !$test[1]; $dl{'dl'} = 'wget'; - $dl{'file'} = ' -q -O '; + $dl{'file'} = " ${quiet}-O "; $dl{'no-ssl'} = ' --no-check-certificate'; - $dl{'spider'} = ' -q --spider'; - $dl{'stdout'} = ' -q -O -'; + $dl{'spider'} = " ${quiet}--spider"; + $dl{'stdout'} = " $quiet -O -"; $dl{'timeout'} = ' -T '; + $dl{'ua'} = ' -U ' . $dl_ua; } elsif ($dl{'fetch'} && check_program('fetch')){ + $quiet = '-q ' if !$test[1]; $dl{'dl'} = 'fetch'; - $dl{'file'} = ' -q -o '; + $dl{'file'} = " ${quiet}-o "; $dl{'no-ssl'} = ' --no-verify-peer'; - $dl{'stdout'} = ' -q -o -'; + $dl{'stdout'} = " ${quiet}-o -"; $dl{'timeout'} = ' -T '; } elsif ( $bsd_type eq 'openbsd' && check_program('ftp') ){ @@ -3683,7 +3697,7 @@ sub update_man { return 0; } if ( ! -w $man_file_location ){ - print "Cannot write to $man_file_location! Are you root?\n"; + print "Cannot write to $man_file_location! Root privileges required.\n"; print "Unable to continue: $man_file_location\n"; return 0; } @@ -4056,6 +4070,16 @@ sub get_options{ else { error_handler('distro-block', $opt); } }, + 'ws|weather-source:s' => sub { + my ($opt,$arg) = @_; + # let api processor handle checks if valid, this + # future proofs this + if ($arg =~ /^[0-9]$/){ + $weather_source = $arg; + } + else { + error_handler('bad-arg',$opt,$arg); + } }, 'weather-unit:s' => sub { my ($opt,$arg) = @_; $arg ||= ''; @@ -4325,6 +4349,15 @@ sub get_options{ $b_usb_tool = 1 }, 'V|version' => sub { $b_version = 1 }, + 'wan-ip-url:s' => sub { + my ($opt,$arg) = @_; + if ($arg && $arg =~ /^(f|ht)tp[s]?:\/\//){ + $wan_url = $arg; + $b_skip_dig = 1 + } + else { + error_handler('bad-arg', $opt, $arg); + }}, 'wm' => sub { $b_wmctrl = 1 }, '<>' => sub { @@ -4334,7 +4367,7 @@ sub get_options{ ## run all these after so that we can change widths, downloaders, etc eval $end if $b_log; CheckRecommends::run() if $b_recommends; - set_downloader() if $b_downloader; + set_downloader() if $b_downloader || $wan_url; # sets for either config or arg here show_version() if $b_version; show_options() if $b_help; $b_man = 0 if (!$b_use_man || $b_no_man_force); @@ -4519,10 +4552,13 @@ sub show_options { ['1', '-w', '--weather', "Local weather data/time. To check an alternate location, see -W."], ['1', '-W', '--weather-location', "[location] Supported options for - [location]: postal code; city, state/country; latitude, longitude. + [location]: postal code[,country]; city, state/country; latitude, longitude. Only use if you want the weather somewhere other than the machine running $self_name. Use only ASCII characters, replace spaces in city/state/country names with '+'. Example:^$self_name^-W^new+york,ny"], + ['1', '', '--weather-source', "[0-9] Change weather data source. 0 uses + a legacy source internally. 1-3 use remote smxi sources. 4-9 may be added + in the future. See man."], ['1', '', '--weather-unit', "Set weather units to metric (m), imperial (i), metric/imperial (mi), or imperial/metric (im)."], ); @@ -4567,7 +4603,7 @@ sub show_options { push @data, @rows; if ( $b_weather ){ @rows = (['2', '-w -W', '', "Wind speed and direction, humidity, pressure, - and (-w only) time zone." ]); + and time zone, if available." ]); push @data, @rows; } @rows = ( @@ -4598,7 +4634,8 @@ sub show_options { ); push @data, @rows; if ( $b_weather ){ - @rows = (['2', '-w -W', '', "Wind chill, dew point, heat index, if available." ]); + @rows = (['2', '-w -W', '', "Snow, rain (last observed hour), cloud cover, + wind chill, dew point, heat index, if available." ]); push @data, @rows; } @rows = ( @@ -4627,7 +4664,7 @@ sub show_options { push @data, @rows; if ( $b_weather ){ @rows = (['2', '-w -W', '', "Location (uses -z/irc filter), weather observation - time, altitude (shows extra lines for data where relevant)." ] ); + time, altitude, if available." ] ); push @data, @rows; } @rows = ( @@ -4714,9 +4751,14 @@ sub show_options { you want a specific output width. Always put this option first in an option list."], ['1', '', '--usb-sys', "Force USB data to use /sys as data source (Linux only)." ], ['1', '', '--usb-tool', "Force USB data to use lsusb as data source (Linux only)." ], + ['1', '', '--wan-ip-url', "[URL] Skips dig, uses supplied URL for WAN IP (-i). + URL output must end in the IP address. See man. + Example:^$self_name^-i^--wan-ip-url^https://yoursite.com/ip.php" ], ['1', '', '--wm', "Force wm: to use wmctrl as data source. Default uses ps." ], ['0', '', '', $line ], ['0', '', '', "Debugging Options:" ], + ['1', '', '--dbg', "Specific debuggers, change often. Only 1 is constant:" ], + ['2', '1', '', "Show downloader output. Turns off quiet mode." ], ['1', '', '--debug', "Triggers debugging modes." ], ['2', '1-3', '', "On screen debugger output." ], ['2', '10', '', "Basic logging." ], @@ -5404,7 +5446,7 @@ sub row_defaults { '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. Are you root?", + 'sensors-ipmi-root' => "Unable to run ipmi sensors. Root privileges required.", 'tool-missing' => "", 'unmounted-data' => "No unmounted partitions found.", 'unmounted-data-bsd' => "No unmounted partition data found for this BSD system.", @@ -5413,6 +5455,7 @@ sub row_defaults { 'unknown-desktop-version' => "ERR-101", 'unknown-dev' => "ERR-102", 'unknown-shell' => "ERR-100", + 'weather-error' => "Error in weather data: $id", 'weather-null' => "No $id found. Internet connection working?", 'xdpyinfo-missing' => '', ); @@ -7883,7 +7926,11 @@ sub cpu_arch { elsif ( $model =~ /^(3A|3E)$/ ) {$arch = 'Ivy Bridge'} elsif ( $model =~ /^(3C|3F|45|46)$/ ) {$arch = 'Haswell'} elsif ( $model =~ /^(3D|47|4F|56)$/ ) {$arch = 'Broadwell'} - elsif ( $model =~ /^(4E|55)$/ ) {$arch = 'Skylake'} # had 9E + elsif ( $model =~ /^(4E)$/ ) {$arch = 'Skylake'} # had 9E, cascade lake also 55 + # need to find stepping for cl, guessing stepping 4 is last for sl + elsif ( $model =~ /^(55)$/ ) { + if ($stepping > 4){$arch = 'Cascade Lake'} + else {$arch = 'Skylake'} } elsif ( $model =~ /^(5C|5F)$/ ) {$arch = 'Goldmont'} elsif ( $model =~ /^(5E)$/ ) {$arch = 'Skylake-S'} elsif ( $model =~ /^(4C)$/ ) {$arch = 'Airmont'} @@ -7896,9 +7943,9 @@ sub cpu_arch { elsif ( $model =~ /^(57)$/ ) {$arch = 'Knights Landing'} elsif ( $model =~ /^(66)$/ ) {$arch = 'Cannon Lake'} elsif ( $model =~ /^(85)$/ ) {$arch = 'Knights Mill'} - elsif ( $model =~ /^(865)$/ ) {$arch = 'Tremont'} + elsif ( $model =~ /^(86)$/ ) {$arch = 'Tremont'} # coming: alder lake; amber lake; cannonlake; cascade lake; coffee lake; - # granite rapids; icelake; meteor lake; saphire rapids; tigerlake, + # cooper lake; granite rapids; icelake; meteor lake; saphire rapids; tigerlake, } # itanium 1 family 7 all recalled elsif ($family eq 'B'){ @@ -8678,7 +8725,7 @@ sub device_vendor { ## These go first because they are the most likely and common ## ['(Crucial|^(FC)?CT|-CT|^M4\b)','Crucial','Crucial',''], ['^(INTEL|SSD(PAM|SA2))','^INTEL','Intel',''], - ['(KINGSTON|DataTraveler|^SMS|^SHS|^SUV|^Ultimate CF)','KINGSTON','Kingston',''], # maybe SHS: SHSS37A SKC SUV + ['(KINGSTON|DataTraveler|DT\s?(DUO|Microduo|101)|^SMS|^SHS|^SUV|^Ultimate CF)','KINGSTON','Kingston',''], # maybe SHS: SHSS37A SKC SUV # must come before samsung MU. NOTE: toshiba can have: TOSHIBA_MK6475GSX: mush: MKNSSDCR120GB_ ['(^MKN|Mushkin)','Mushkin','Mushkin',''], # MKNS # MU = Multiple_Flash_Reader too risky: |M[UZ][^L] HD103SI HD start risky @@ -8697,14 +8744,14 @@ sub device_vendor { ['^ATP','^ATP[\s\-]','ATP',''], # Force MP500 ['^(Corsair|Force\s|Voyager)','^Corsair','Corsair',''], - ['^(FUJITSU|MHV|MP)','^FUJITSU','Fujitsu',''], + ['^(FUJITSU|MH[VY]|MP)','^FUJITSU','Fujitsu',''], # note: 2012: wdc bought hgst - ['^(HGST|Touro)','^HGST','HGST (Hitachi)',''], # HGST HUA + ['^(HGST|Touro|5450)','^HGST','HGST (Hitachi)',''], # HGST HUA ['^(Hitachi|HD[ST]|DK[0-9]|IC|HT|HU)','^Hitachi','Hitachi',''], ['^Hoodisk','^Hoodisk','Hoodisk',''], # vb: VB0250EAVER but clashes with vbox; HP_SSD_S700_120G ;GB0500EAFYL GB starter too generic? ['^(HP\b|MB0|G[BJ]0|v[0-9]{3}[ow])','^HP','HP',''], - ['^(LSD|Lexar|JumpDrive)','^Lexar','Lexar',''], # mmc-LEXAR_0xb016546c + ['^(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|TALOS2|TMSC|TRSAK)','^OCZ[\s\-]','OCZ',''], ['^OWC','^OWC[\s\-]','OWC',''], @@ -8729,6 +8776,9 @@ sub device_vendor { ['^CHN\b','','Zheino',''], ['^Colorful\b','^Colorful','Colorful',''], ['^CSD','^CSD','CSD',''], + ['^(Dane-?Elec|Z Mate)','^Dane-?Elec','DaneElec',''], + # Daplink vfs is an ARM software thing + ['^DeLOCK','^Delock(\s?products)?','Delock',''], ['^DGM','^DGM\b','DGM',''], ['^DIGITAL\s?FILM','DIGITAL\s?FILM','Digital Film',''], ['^DREVO\b','^DREVO','Drevo',''], @@ -8746,7 +8796,7 @@ sub device_vendor { ['^Gigabyte','^Gigabyte','Gigabyte',''], # SSD ['^Gigastone','^Gigastone','Gigastone',''], ['^Gloway','^Gloway','Gloway',''], - ['^GOODRAM','^GOODRAM','GOODRAM',''], + ['^(GOODRAM|IR SSD)','^GOODRAM','GOODRAM',''], # supertalent also has FM: |FM ['^(G[\.]?SKILL)','^G[\.]?SKILL','G.SKILL',''], ['^HUAWEI','^HUAWEI','Huawei',''], @@ -8818,6 +8868,7 @@ sub device_vendor { ['^(TDK|TF[1-9][0-9])','^TDK','TDK',''], ['^TEAC','^TEAC','TEAC',''], ['^TEAM','^TEAM( Group)?','Team',''], + ['^Tigo','^Tigo','Tigo',''], ['^TopSunligt','^TopSunligt','TopSunligt',''], # is this a typo? hard to know ['^TopSunlight','^TopSunlight','TopSunlight',''], ['^(TS|Transcend|JetFlash)','^Transcend','Transcend',''], @@ -9944,7 +9995,7 @@ sub machine_data_soc { $temp[1] = main::dmi_cleaner($temp[1]); $soc_machine{'device'} = main::cleaner($temp[1]); } - elsif (/^(system type)\s*:/i){ + elsif (/^(system type|model)\s*:/i){ @temp = split /\s*:\s*/, $_; $temp[1] = main::dmi_cleaner($temp[1]); $soc_machine{'model'} = main::cleaner($temp[1]); @@ -10743,19 +10794,22 @@ sub if_ip { # dig +short +time=1 +tries=1 myip.opendns.com. A @208.67.222.222 sub wan_ip { eval $start if $b_log; - my (@data,$ip); + my (@data,$ip,$ua); my $num = 0; # time: 0.06 - 0.07 seconds - if (my $program = main::check_program('dig')){ + if (!$b_skip_dig && (my $program = main::check_program('dig') )){ $ip = (main::grabber("$program +short +time=1 +tries=1 myip.opendns.com \@resolver1.opendns.com 2>/dev/null"))[0]; } else { # note: tests: akamai: 0.055 - 0.065 icanhazip.com: 0.177 0.164 # smxi: 0.525, so almost 10x slower. Dig is fast too # leaving smxi as last test because I know it will always be up. - my @urls = qw( http://whatismyip.akamai.com/ http://icanhazip.com/ https://smxi.org/opt/ip.php); + # --wan-ip-url replaces values with user supplied arg + my @urls = (!$wan_url) ? qw( http://whatismyip.akamai.com/ + http://icanhazip.com/ https://smxi.org/opt/ip.php) : ($wan_url); foreach (@urls){ - $ip = main::download_file('stdout',$_); + $ua = 'ip' if $_ =~ /smxi/; + $ip = main::download_file('stdout',$_,'',$ua); if ($ip){ # print "$_\n"; chomp $ip; @@ -15302,7 +15356,7 @@ sub get { sub create_output { eval $start if $b_log; my $num = 0; - my (@data,@location,@rows,%weather,); + my (@data,@location,@rows,$value,%weather,); my ($conditions) = ('NA'); if ($show{'weather-location'}){ my $location_string; @@ -15331,6 +15385,11 @@ sub create_output { } } %weather = get_weather(@location); + if ($weather{'error'}) { + return @rows = ({ + main::key($num++,'Message') => main::row_defaults('weather-error',$weather{'error'}), + }); + } if (!$weather{'weather'}) { return @rows = ({ main::key($num++,'Message') => main::row_defaults('weather-null','weather data'), @@ -15348,33 +15407,54 @@ sub create_output { my $wind = wind_output($weather{'wind'},$weather{'wind-direction'},$weather{'wind-mph'},$weather{'wind-ms'}, $weather{'wind-gust-mph'},$weather{'wind-gust-ms'}); $rows[0]{main::key($num++,'Wind')} = $wind; - $rows[0]{main::key($num++,'Humidity')} = $weather{'humidity'}; + if ($extra > 1){ + if (defined $weather{'cloud-cover'}){ + $rows[0]{main::key($num++,'Cloud Cover')} = $weather{'cloud-cover'} . '%'; + } + if ($weather{'rain-1h-mm'} && defined $weather{'rain-1h-in'} ){ + $value = unit_output('',$weather{'rain-1h-mm'},'mm',$weather{'rain-1h-in'},'in'); + $rows[0]{main::key($num++,'Rain')} = $value; + } + if ($weather{'snow-1h-mm'} && defined $weather{'snow-1h-in'} ){ + $value = unit_output('',$weather{'snow-1h-mm'},'mm',$weather{'snow-1h-in'},'in'); + $rows[0]{main::key($num++,'Snow')} = $value; + } + } + $rows[0]{main::key($num++,'Humidity')} = $weather{'humidity'} . '%'; + if ($extra > 1){ + if ($weather{'dewpoint'} || (defined $weather{'dewpoint-c'} && defined $weather{'dewpoint-f'})){ + $value = unit_output($weather{'dewpoint'},$weather{'dewpoint-c'},'C',$weather{'dewpoint-f'},'F'); + $rows[0]{main::key($num++,'Dew Point')} = $value; + } + } $rows[0]{main::key($num++,'Pressure')} = $pressure; } if ($extra > 1){ - if ($weather{'heat-index'}){ - my $heat = unit_output($weather{'heat-index'},$weather{'heat-index-c'},'C',$weather{'heat-index-f'},'F'); - $rows[0]{main::key($num++,'Heat Index')} = $heat; + if ($weather{'heat-index'} || (defined $weather{'heat-index-c'} && defined $weather{'heat-index-f'})){ + $value = unit_output($weather{'heat-index'},$weather{'heat-index-c'},'C',$weather{'heat-index-f'},'F'); + $rows[0]{main::key($num++,'Heat Index')} = $value; } - if ($weather{'windchill'}){ - my $chill = unit_output($weather{'windchill'},$weather{'windchill-c'},'C',$weather{'windchill-f'},'F'); - $rows[0]{main::key($num++,'Wind Chill')} = $chill ; - } - if ($weather{'dewpoint'}){ - my $dew = unit_output($weather{'dewpoint'},$weather{'dewpoint-c'},'C',$weather{'dewpoint-f'},'F'); - $rows[0]{main::key($num++,'Dew Point')} = $dew; + if ($weather{'windchill'} || (defined $weather{'windchill-c'} && defined $weather{'windchill-f'})){ + $value = unit_output($weather{'windchill'},$weather{'windchill-c'},'C',$weather{'windchill-f'},'F'); + $rows[0]{main::key($num++,'Wind Chill')} = $value; } } if ($extra > 2){ if (!$show{'filter'}){ - $rows[0]{main::key($num++,'Location')} = $location[1]; - $rows[0]{main::key($num++,'altitude')} = elevation_output($weather{'elevation-m'},$weather{'elevation-ft'}); + $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'}); + } } } $rows[0]{main::key($num++,'Current Time')} = $weather{'date-time'}; if ($extra > 2){ + $weather{'observation-time-local'} = 'N/A' if !$weather{'observation-time-local'}; $rows[0]{main::key($num++,'Observation Time')} = $weather{'observation-time-local'}; } + if ($weather{'api-source'}){ + $rows[0]{main::key($num++,'Source')} = $weather{'api-source'}; + } eval $end if $b_log; return @rows; } @@ -15407,16 +15487,16 @@ sub unit_output { eval $start if $b_log; my ($primary,$metric,$m_unit,$imperial,$i_unit) = @_; my $result = ''; - if ($metric && $imperial && $weather_unit eq 'mi' ){ + if (defined $metric && defined $imperial && $weather_unit eq 'mi' ){ $result = "$metric $m_unit ($imperial $i_unit)"; } - elsif ($metric && $imperial && $weather_unit eq 'im' ){ + elsif (defined $metric && defined $imperial && $weather_unit eq 'im' ){ $result = "$imperial $i_unit ($metric $m_unit)"; } - elsif ($metric && $weather_unit eq 'm' ){ + elsif (defined $metric && $weather_unit eq 'm' ){ $result = "$metric $m_unit"; } - elsif ($imperial && $weather_unit eq 'i' ){ + elsif (defined $imperial && $weather_unit eq 'i' ){ $result = "$imperial $i_unit"; } elsif ($primary){ @@ -15495,48 +15575,33 @@ sub get_weather { my $loc_name = lc($location[0]); $loc_name =~ s/-\/|\s|,/-/g; $loc_name =~ s/--/-/g; - my $file_cached = "$user_data_dir/weather-$loc_name.txt"; + my $file_cached = "$user_data_dir/weather-$loc_name-$weather_source.txt"; if (-f $file_cached){ @weather_data = main::reader($file_cached); $freshness = (split /\^\^/, $weather_data[0])[1]; #print "$now:$freshness\n"; } if (!$freshness || $freshness < ($now - 90) ) { - @weather_data = (); # reset so we don't write the previous data to file!! - my $url = "http://api.wunderground.com/auto/wui/geo/WXCurrentObXML/index.xml?query=$location[0]"; - my $temp; -# { -# #my $file2 = "$ENV{'HOME'}/bin/scripts/inxi/data/weather/weather-1.xml"; -# # my $file2 = "$ENV{'HOME'}/bin/scripts/inxi/data/weather/feed-oslo-1.xml"; -# local $/; -# my $file = "$ENV{'HOME'}/bin/scripts/inxi/data/weather/weather-1.xml"; -# open my $fh, '<', $file or die "can't open $file: $!"; -# $temp = <$fh>; -# } - $temp = main::download_file('stdout',$url); - $temp =~ s/\r|\n\n/\n/g; - my @weather_temp = split /\n/, $temp; - foreach (@weather_temp){ - chomp $_; - $_ =~ s/<\/[^>]+>//; - $_ =~ s/.*icon.*|\r//g; - $_ =~ s/\s\s/ /g; - $_ =~ s/^\s+|\s+$//g; - $_ =~ s/>/^^/; - $_ =~ s/^<|NA$//g; - $_ =~ s/^(current|credit|terms|image|title|link|.*_url).*//; - push @weather_data, $_ if $_ !~ /^\s*$/; - } - unshift (@weather_data,("timestamp^^$now")); - main::writer($file_cached,\@weather_data); - #print "$file_cached: download/cleaned\n"; + @weather_data = download_weather($now,$file_cached,@location); } #print join "\n", @weather_data, "\n"; # NOTE: because temps can be 0, we can't do if value tests foreach (@weather_data){ my @working = split /\s*\^\^\s*/,$_; next if ! defined $working[1] || $working[1] eq ''; - if ( $working[0] eq 'dewpoint_string' ){ + if ( $working[0] eq 'api_source' ){ + $weather{'api-source'} = $working[1]; + } + elsif ( $working[0] eq 'city' ){ + $weather{'city'} = $working[1]; + } + elsif ( $working[0] eq 'cloud_cover' ){ + $weather{'cloud-cover'} = $working[1]; + } + elsif ( $working[0] eq 'country' ){ + $weather{'country'} = $working[1]; + } + elsif ( $working[0] eq 'dewpoint_string' ){ $weather{'dewpoint'} = $working[1]; $working[1] =~ /^([0-9\.]+)\sF\s\(([0-9\.]+)\sC\)/; $weather{'dewpoint-c'} = $2;; @@ -15548,12 +15613,15 @@ sub get_weather { elsif ( $working[0] eq 'dewpoint_f' ){ $weather{'dewpoint-f'} = $working[1]; } - # there are two elevations, we want the first one + # WU: there are two elevations, we want the first one elsif (!$weather{'elevation-m'} && $working[0] eq 'elevation'){ # note: bug in source data uses ft for meters, not 100% of time, but usually $weather{'elevation-m'} = $working[1]; $weather{'elevation-m'} =~ s/\s*(ft|m).*$//; } + elsif ( $working[0] eq 'error' ){ + $weather{'error'} = $working[1]; + } elsif ( $working[0] eq 'heat_index_string' ){ $weather{'heat-index'} = $working[1]; $working[1] =~ /^([0-9\.]+)\sF\s\(([0-9\.]+)\sC\)/; @@ -15567,6 +15635,7 @@ sub get_weather { $weather{'heat-index-f'} = $working[1]; } elsif ( $working[0] eq 'relative_humidity' ){ + $working[1] =~ s/%$//; $weather{'humidity'} = $working[1]; } elsif ( $working[0] eq 'local_time' ){ @@ -15576,7 +15645,7 @@ sub get_weather { $weather{'local-epoch'} = $working[1]; } elsif ( $working[0] eq 'observation_time_rfc822' ){ - $weather{'observation-time-gmt'} = $working[1]; + $weather{'observation-time-rfc822'} = $working[1]; } elsif ( $working[0] eq 'observation_epoch' ){ $weather{'observation-epoch'} = $working[1]; @@ -15594,6 +15663,21 @@ sub get_weather { elsif ( $working[0] eq 'pressure_in' ){ $weather{'pressure-in'} = $working[1]; } + elsif ( $working[0] eq 'rain_1h_mm' ){ + $weather{'rain-1h-mm'} = $working[1]; + } + elsif ( $working[0] eq 'rain_1h_in' ){ + $weather{'rain-1h-in'} = $working[1]; + } + elsif ( $working[0] eq 'snow_1h_mm' ){ + $weather{'snow-1h-mm'} = $working[1]; + } + elsif ( $working[0] eq 'snow_1h_in' ){ + $weather{'snow-1h-in'} = $working[1]; + } + elsif ( $working[0] eq 'state_name' ){ + $weather{'state'} = $working[1]; + } elsif ( $working[0] eq 'temperature_string' ){ $weather{'temp'} = $working[1]; $working[1] =~ /^([0-9\.]+)\sF\s\(([0-9\.]+)\sC\)/; @@ -15609,6 +15693,9 @@ sub get_weather { elsif ( $working[0] eq 'temp_c' ){ $weather{'temp-c'} = $working[1]; } + elsif ( $working[0] eq 'timezone' ){ + $weather{'timezone'} = $working[1]; + } elsif ( $working[0] eq 'visibility' ){ $weather{'visibility'} = $working[1]; } @@ -15656,14 +15743,26 @@ sub get_weather { } } if ($show{'weather-location'}){ - $weather{'observation-time-local'} =~ /^(.*)\s([\S]+)$/; - $tz = $2; + if ($weather{'observation-time-local'} && + $weather{'observation-time-local'} =~ /^(.*)\s([a-z_]+\/[a-z_]+)$/i){ + $tz = $2; + } + if (!$tz && $weather{'timezone'}){ + $tz = $weather{'timezone'}; + $weather{'observation-time-local'} .= ' (' . $weather{'timezone'} . ')' if $weather{'observation-time-local'}; + } # very clever trick, just make the system think it's in the # remote timezone for this local block only - local $ENV{'TZ'} = $tz; + local $ENV{'TZ'} = $tz if $tz; $date_time = POSIX::strftime "%c", localtime(); $date_time = test_local_date($date_time,'',''); $weather{'date-time'} = $date_time; + # only wu has rfc822 value, and we want the original observation time then + if ($weather{'observation-epoch'} && $tz){ + $date_time = POSIX::strftime "%Y-%m-%d %T ($tz %z)", localtime($weather{'observation-epoch'}); + $date_time = test_local_date($date_time,$show{'weather-location'},$weather{'observation-epoch'}); + $weather{'observation-time-local'} = $date_time; + } } else { $date_time = POSIX::strftime "%c", localtime(); @@ -15680,6 +15779,50 @@ sub get_weather { return %weather; eval $end if $b_log; } +sub download_weather { + eval $start if $b_log; + my ($now,$file_cached,@location) = @_; + my (@weather,@weather_temp,$temp,$ua,$url); + if ($weather_source == 0){ + $url = "http://api.wunderground.com/auto/wui/geo/WXCurrentObXML/index.xml?query=$location[0]"; + } + else { + $url = "https://smxi.org/opt/xr2.php?loc=$location[0]&src=$weather_source"; + } + $ua = 'weather' if $url =~ /smxi/; +# { +# #my $file2 = "$ENV{'HOME'}/bin/scripts/inxi/data/weather/weather-1.xml"; +# # my $file2 = "$ENV{'HOME'}/bin/scripts/inxi/data/weather/feed-oslo-1.xml"; +# local $/; +# my $file = "$ENV{'HOME'}/bin/scripts/inxi/data/weather/weather-1.xml"; +# open my $fh, '<', $file or die "can't open $file: $!"; +# $temp = <$fh>; +# } + $temp = main::download_file('stdout',$url,'',$ua); + $temp =~ s/\r|\n\n/\n/g if $weather_source == 0; + @weather_temp = split /\n/, $temp; + if ($weather_source == 0){ + foreach (@weather_temp){ + chomp $_; + $_ =~ s/<\/[^>]+>//; + $_ =~ s/.*icon.*|\r//g; + $_ =~ s/\s\s/ /g; + $_ =~ s/^\s+|\s+$//g; + $_ =~ s/>/^^/; + $_ =~ s/^<|NA$//g; + $_ =~ s/^(current|credit|terms|image|title|link|.*_url).*//; + push @weather, $_ if $_ !~ /^\s*$/; + } + } + else { + @weather = @weather_temp; + } + unshift (@weather,("timestamp^^$now")); + main::writer($file_cached,\@weather); + #print "$file_cached: download/cleaned\n"; + eval $end if $b_log; + return @weather; +} # resolve wide character issue, if detected, switch to iso # date format, we won't try to be too clever here. sub test_local_date { @@ -15694,6 +15837,7 @@ sub test_local_date { $date_time = POSIX::strftime "%Y-%m-%d %H:%M:%S", localtime(); } } + $date_time =~ s/\s+$//; #print "2: $date_time\n"; return $date_time; } @@ -15786,6 +15930,17 @@ sub get_location { eval $end if $b_log; return @location; } +sub complete_location { + eval $start if $b_log; + my ($location,$city,$state,$country) = @_; + 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; + } + eval $end if $b_log; + return $location; +} } #### ------------------------------------------------------------------- @@ -16710,7 +16865,7 @@ sub get_linux_distro { } if ($extra > 0){ my $base_debian_version_distro = 'sidux'; - my $base_debian_version_osr = '\belive|lmde|neptune|parrot|pureos|sparky|tails'; + my $base_debian_version_osr = '\belive|lmde|neptune|parrot|pureos|septor|sparky|tails'; my $base_default = 'antix-version|mx-version'; # osr has base ids my $base_issue = 'bunsen'; # base only found in issue my $base_manual = 'blankon|deepin|kali'; # synthesize, no direct data available @@ -17410,12 +17565,15 @@ sub get_pci_vendor { eval $start if $b_log; my ($device, $subsystem) = @_; return if !$subsystem; - my ($vendor,$sep) = ('',''); + my ($vendor,$sep,$temp) = ('','',''); # get rid of any [({ type characters that will make regex fail $subsystem = regex_cleaner($subsystem); my @data = split /\s+/, $subsystem; + # when using strings in patterns for regex have to escape them foreach (@data){ - if ($device !~ /\b$_\b/){ + $temp = $_; + $temp =~ s/(\+|\$|\?|\^|\*)/\\$1/g; + if ($device !~ m|\b$temp\b|){ $vendor .= $sep . $_; $sep = ' '; } diff --git a/inxi.1 b/inxi.1 index 2380dcd..dfa8e7d 100644 --- a/inxi.1 +++ b/inxi.1 @@ -1,4 +1,4 @@ -.TH INXI 1 "2018\-12\-31" inxi "inxi manual" +.TH INXI 1 "2019\-02\-06" inxi "inxi manual" .SH NAME inxi \- Command line system information script for console and IRC .SH SYNOPSIS @@ -446,15 +446,16 @@ optical drives. .TP .B \-v 6 \- Adds full mounted partition data (\fB\-p\fR), unmounted partition data (\fB\-o\fR), -optical drive data (\fB\-d\fR), USB (\fB\-\-usb\fR); triggers \fB\-xx\fR extra data option. +optical drive data (\fB\-d\fR), USB (\fB\-\-usb\fR); triggers \fB\-xx\fR extra data +option. .TP .B \-v 7 \- Adds network IP data (\fB\-i\fR); triggers \fB\-xxx\fR .TP .B \-v 8 -\- All system data available. Adds Repos (\fB\-r\fR), PCI slots (\fB\-\-slots\fR), processes -(\fB\-tcm\fR), admin (\fB\-\-admin\fR). Useful for testing output and to see what data -you can get from your system. +\- All system data available. Adds Repos (\fB\-r\fR), PCI slots (\fB\-\-slots\fR), +processes (\fB\-tcm\fR), admin (\fB\-\-admin\fR). Useful for testing output and to +see what data you can get from your system. .TP .B \-w\fR,\fB \-\-weather\fR Adds weather line. Note, this depends on an unreliable API so it may not always @@ -462,14 +463,28 @@ be working in the future. To get weather for an alternate location, use \fB\-W\fR. See also \fB\-x\fR, \fB\-xx\fR, \fB\-xxx\fR options. Please note that your distribution's maintainer may chose to disable this feature. .TP -.B \-W\fR,\fB \-\-weather\-location \fR -Get weather/time for an alternate location. Accepts postal/zip code, +.B \-W\fR, \fB\-\-weather\-location \fR +Get weather/time for an alternate location. Accepts postal/zip code[, country], city,state pair, or latitude,longitude. Note: city/country/state names must not -contain spaces. Replace spaces with '\fB+\fR' sign. Don't place spaces around any commas. -Use only ASCII letters in city/state/country names, sorry. +contain spaces. Replace spaces with '\fB+\fR' sign. Don't place spaces around +any commas. Postal code is not reliable except for North America. -Examples: \fB\-W 95623\fR OR \fB\-W Boston,MA\fR OR \fB\-W45.5234,\-122.6762\fR -OR \fB\-W new+york,ny\fR OR \fB\-W bodo,norway\fR. +Use only ASCII letters in city/state/country names. + +Examples: \fB\-W 95623,usa\fR OR \fB\-W Boston,MA\fR OR +\fB\-W 45.5234,\-122.6762\fR OR \fB\-W new+york,ny\fR OR \fB\-W bodo,norway\fR. +.TP +.B \-\-weather\-source\fR, \fB\-\-ws \fR +[\fB0\-9\fR] Switches weather data source. 0 uses a legacy source which may vanish +any day. \fB1\-9\fR use different sources from a remote API. \fB1\fR is the same as \fB0 currently. +\fB2\fR has less data than \fB1\fR or \fB3\fR, and may not support city / country names with spaces +(even if you use the \fB+\fR sign instead of space). \fB3\fR offers pretty good data, but +may not have all small city names for \fB\-W\fR. \fB4\-9\fR may be added at a future +point, but are not currently supported. + +Note that the actual remote sources may change without notice, but that will have +no real impact on inxi. Also, additional options may be added in the future, between +0 and 9. .TP .B \-\-weather\-unit \fR [\fBm\fR|\fBi\fR|\fBmi\fR|\fBim\fR] Sets weather units to metric (\fBm\fR), imperial (\fBi\fR), @@ -747,7 +762,10 @@ if \fBps\fR tests fail to find data. \- Adds vendor:chip id. .TP .B \-xx \-w\fR,\fB \-W\fR -\- Adds wind chill, heat index, and dew point if any of these are available. +\- Adds wind chill, heat index, and dew point, if available. + +\- Adds cloud cover, rain, snow (amount in previous hour to observation time), +if available. .TP .B \-xxx \-A\fR \- Adds, if present, serial number. @@ -826,7 +844,8 @@ lxpanel, xfce4\-panel, lxqt\-panel, tint2, cairo-dock, trayer, and many others. \- Adds, if available, USB speed in \fBMbits/s\fR or \fBGbits/s\fR. .TP .B \-xxx \-w\fR,\fB \-W\fR -\- Adds location (city state country), altitude, weather observation time. +\- Adds location (city state country), observation altitude (if available), +weather observation time (if available). .SH ADMIN EXTRA DATA OPTIONS These options are triggered with \fB\-\-admin\fR or \fB\-a\fR. Admin options are @@ -1040,6 +1059,18 @@ instead of \fBlsusb\fR. Forces the USB data generator to use \fBlsusb\fR as data source. Overrides \fBUSB_SYS\fR in user configuration file(s). +.TP +.B \-\-wan\-ip\-url [URL]\fR +Force \fB\-i\fR to use supplied URL as WAN IP source. Overrides dig or +default IP source urls. URL must start with http[s] or ftp. + +The IP address from the URL must be the last item on the last (non-empty) line +of the page content source code. + +Same as configuration value (example): + +\fBWAN_IP_URL='https://mysite.com/ip.php'\fR + .TP .B \-\-wm\fR Force \fBSystem\fR item \fBwm\fR to use \fBwmctrl\fR as data source, @@ -1047,6 +1078,12 @@ override default \fBps\fR source. .SH DEBUGGING OPTIONS +.TP +.B \-\-dbg 1\fR +\- Debug downloader failures. Turns off silent/quiet mode for curl, wget, and +fetch. Shows more downloader action information. Shows some more information +for Perl downloader. + .TP .B \-\-debug [1\-3]\fR \- On screen debugger output. Output varies depending on current needs @@ -1238,6 +1275,19 @@ above configuration page on smxi.org for full info. \fBUSB_SYS\fR Forces all USB data to use \fB/sys\fR instead of \fBlsusb\fR. +\fBWAN_IP_URL\fR Forces \fB\-i\fR to use supplied URL, and to not use dig (dig is +generally much faster). URL must begin with http or ftp. Note that if you use this, +the downloader set tests will run each time you start inxi whether a downloader feature +is going to be used or not. + +The IP address from the URL must be the last item on the last (non-empty) line of +the URL's page content source code. + +Same as \fB\-\-wan\-ip\-url [URL]\fR + +\fBWEATHER_SOURCE\fR Values: [\fB0-9\fR]. Same as \fB\-\-weather\-source\fR. Values +4-9 are not currently supported, but this can change at any time. + \fBWEATHER_UNIT\fR Values: [\fBc\fR|\fBf\fR|\fBcf\fR|\fBfc\fR]. Same as \fB\-\-weather\-unit\fR. .TP diff --git a/inxi.changelog b/inxi.changelog index 0bbbf42..e231d68 100644 --- a/inxi.changelog +++ b/inxi.changelog @@ -1,3 +1,69 @@ +===================================================================================== +Version: 3.0.31 +Patch: 00 +Date: 2019-02-06 +----------------------------------- +Changes: +----------------------------------- +New version, new man page. Big update! Get it in before your freeze!! + +Bugs: +1. Maybe the vendor/product regex, which when + was used, would put out +errors. +2. Maybe Fix 4, since that could lead to incorrect behavior when sudo +is involved depending on sudo configuration. +3. BIG: current inxi weather will probably fail if not updated to this or +newer versions!! Not an inxi bug per se, but your users will see it as one. + +Fixes: +1. Fixed Patriot disk ID. +2. Fixes for PPC board handling. +3. Regex cleaner fixes, this could lead to error in special cases of product +vendor names. +4. crazy from frugalware pointed out that $b_root detection was flawed, and +relied on a bad assumption, particularly for sudo. As usual, he's right, that +is now corrected, and uses $< Perl native to determine UID. + +Enhancements: +1. Added septor to Debian system base. +2. Removed quiet filters for downloaders when using --dbg 1, now you see the +entire download action for curl/wget downloads. This went along with +issue # 174 +3. New feature: --wan-ip-url. This closed issue #174. Also has user config +option: WAN_IP_URL as well to make changes permanent. +4. Added --dbg 1 to man and help. The other --dbg options are random and can +change, but --dbg 1 is always for downloading, so might as well tell people +about it. +5. To anticipate the loss of a major weather API, inxi is redone to use smxi.org +based robust API. This also allows for a new switch, --weather-source (or --ws +for shorter version), options 0-9, which will trigger different APIs on smxi.org. +Added WEATHER_SOURCE configuration option as well. Note that 4-9 are not +currently active. Also added in better error handling for weather. +The main benefit here is that inxi is now largely agnostic to the weather APIs +used, and those can be changed with no impact to inxi users who are running +frozen pool inxi's, or who have not updated their inxi versions. + +NOTE: all inxi versions older than 3.0.31 will probably fail for weather +quite soon. So update your inxi version in your repos!! +6. More disk vendors IDs and matches. Thanks linuxlite hardware database. +7. Going along with weather changes, added, if present, cloud cover, rain, and +snow reports. Those are for previously observed hour. +8. Small change to Intel CPU architecture, taking a guess on stepping for +skylake/Cascade lake ID. Guessing if stepping is > 4, it's cascade lake. But +could not find this documented, so it's a guess. At worst, it means that Cascade +lake, which must be a later steppingi than 4, will not be ID'ed as skylake. +9. Documentation updates for data sources. + +Changes: +1. inxi now uses a new system to get weather data. There is no longer a risk +of weather failing if the API used locally in inxi fails or goes away. This +change should be largely invisible to casual users. +2. In weather, moved dewpoint to be after humidity, which makes a little more +sense. + +----------------------------------- +-- Harald Hope - Wed, 06 Feb 2019 18:09:53 -0800 + ===================================================================================== Version: 3.0.30 Patch: 00